Scudo — это динамический распределитель памяти пользовательского режима, или распределитель кучи , разработанный для обеспечения устойчивости к уязвимостям, связанным с кучей (таким как переполнение буфера в куче , использование после освобождения и двойное освобождение ), сохраняя при этом производительность. Он предоставляет стандартные примитивы выделения и освобождения памяти C (такие как malloc и free), а также примитивы C++ (такие как new и delete).
Scudo — это скорее средство смягчения последствий, чем полноценный детектор ошибок памяти, такой как AddressSanitizer (ASan) .
Начиная с выпуска Android 11, scudo используется для всего нативного кода (за исключением устройств с небольшим объёмом памяти, где по-прежнему используется jemalloc). Во время выполнения все операции выделения и освобождения нативной кучи обслуживаются Scudo для всех исполняемых файлов и их библиотечных зависимостей, и процесс прерывается при обнаружении повреждения или подозрительного поведения в куче.
Scudo — это проект с открытым исходным кодом , являющийся частью проекта LLVM compiler-rt. Документация доступна по адресу https://llvm.org/docs/ScudoHardenedAllocator.html . Среда выполнения Scudo входит в состав набора инструментов Android, а её поддержка была добавлена в Soong и Make для лёгкого включения распределителя в двоичном файле.
Вы можете включить или отключить дополнительное смягчение в распределителе, используя параметры, описанные ниже.
Настройка
Некоторые параметры распределителя можно определить для каждого процесса несколькими способами:
- Статически: определите в программе функцию
__scudo_default_options
, которая возвращает строку параметров для анализа. Эта функция должна иметь следующий прототип:extern "C" const char *__scudo_default_options()
. - Динамически: используйте переменную окружения
SCUDO_OPTIONS
содержащую строку параметров для анализа. Параметры, определённые таким образом, переопределяют любое определение, сделанное через__scudo_default_options
.
Доступны следующие варианты.
Вариант | 64-бит по умолчанию | 32-бит по умолчанию | Описание |
---|---|---|---|
QuarantineSizeKb | 256 | 64 | Размер карантина (в КБ), используемый для задержки фактического освобождения фрагментов. Меньшее значение может уменьшить использование памяти, но снизит эффективность мер; отрицательное значение возвращает значения по умолчанию. Установка этого параметра и ThreadLocalQuarantineSizeKb в ноль полностью отключает карантин. |
QuarantineChunksUpToSize | 2048 | 512 | Размер (в байтах), до которого фрагменты могут быть помещены в карантин. |
ThreadLocalQuarantineSizeKb | 64 | 16 | Размер кэша (в КБ) на поток, используемого для разгрузки глобального карантина. Более низкое значение может снизить использование памяти, но может увеличить конкуренцию в глобальном карантине. Установка этого значения и QuarantineSizeKb в ноль полностью отключает карантин. |
DeallocationTypeMismatch | false | false | Включает сообщения об ошибках при malloc/delete, new/free, new/delete[] |
DeleteSizeMismatch | true | true | Включает сообщение об ошибках при несоответствии размеров новых и удаленных объектов. |
ZeroContents | false | false | Позволяет сохранять нулевое содержимое фрагментов при выделении и освобождении. |
allocator_may_return_null | false | false | Указывает, что распределитель может вернуть значение null при возникновении восстановимой ошибки вместо завершения процесса. |
hard_rss_limit_mb | 0 | 0 | Когда RSS процесса достигает этого предела, процесс завершается. |
soft_rss_limit_mb | 0 | 0 | Когда RSS процесса достигает этого предела, дальнейшие выделения завершаются неудачей или возвращают null (в зависимости от значения allocator_may_return_null ) до тех пор, пока RSS не вернется к норме, чтобы разрешить новые выделения. |
allocator_release_to_os_interval_ms | 5000 | Н/Д | Влияет только на 64-битный распределитель. Если установлено, пытается освободить неиспользуемую память для ОС, но не чаще указанного интервала (в миллисекундах). Если значение отрицательное, память не освобождается для ОС. |
abort_on_error | true | true | Если установлено, инструмент вызывает abort() вместо _exit() после вывода сообщения об ошибке. |
Проверка
В настоящее время нет тестов CTS, специально предназначенных для Scudo. Вместо этого убедитесь, что тесты CTS проходят как с включённым Scudo, так и без него для данного исполняемого файла, чтобы убедиться, что он не влияет на работу устройства.
Поиск неисправностей
При обнаружении неустранимой проблемы распределитель выводит сообщение об ошибке в стандартный дескриптор ошибок и завершает процесс. В системный журнал заносятся трассировки стека, приведшие к завершению процесса. Вывод обычно начинается с Scudo ERROR:
за которым следует краткое описание проблемы с указанием возможных ссылок.
Вот список текущих сообщений об ошибках и их потенциальных причин:
-
corrupted chunk header
: проверка контрольной суммы заголовка фрагмента не удалась. Вероятно, это связано с одной из двух причин: заголовок был перезаписан (частично или полностью), либо переданный функции указатель не является фрагментом. -
race on chunk header
: два разных потока одновременно пытаются манипулировать одним и тем же заголовком. Обычно это признак гонки или общего отсутствия блокировки при выполнении операций над этим фрагментом. -
invalid chunk state
: фрагмент не находится в ожидаемом состоянии для данной операции, например, он не выделен при попытке освобождения или не помещён в карантин при попытке его утилизации. Типичная причина этой ошибки — двойное освобождение. -
misaligned pointer
: основные требования к выравниванию строго соблюдаются: 8 байт на 32-битных платформах и 16 байт на 64-битных. Если указатель, переданный в наши функции, не соответствует этим требованиям, указатель, переданный в одну из функций, не выровнен. -
allocation type mismatch
: при включении этой опции функция освобождения, вызываемая для фрагмента, должна соответствовать типу функции, которая была вызвана для его выделения. Такое несоответствие может привести к проблемам безопасности. -
invalid sized delete
: при использовании оператора удаления с заданным размером C++14 и включенной дополнительной проверке возникает несоответствие между размером, переданным при освобождении фрагмента, и размером, запрошенным при его выделении. Обычно это связано с ошибкой компилятора или путаницей типов освобождаемого объекта. -
RSS limit exhausted
: Превышен максимально допустимый размер RSS, указанный опционально.
Если вы отлаживаете сбой в самой ОС, вы можете использовать сборку HWASan для ОС . Если вы отлаживаете сбой в приложении, вы также можете использовать сборку HWASan для приложения .