Скудо

Scudo — это динамический пользовательский распределитель памяти, или распределитель кучи , разработанный для защиты от уязвимостей, связанных с кучей (таких как переполнение буфера в куче , использование памяти после освобождения и двойное освобождение ), при сохранении производительности. Он предоставляет стандартные примитивы выделения и освобождения памяти в C (такие как malloc и free), а также примитивы C++ (такие как new и delete).

Scudo — это скорее средство смягчения последствий ошибок, чем полноценный детектор ошибок памяти, подобный AddressSanitizer (ASan) .

Начиная с Android 11, для всего нативного кода используется scudo (за исключением устройств с малым объемом памяти, где по-прежнему используется jemalloc). Во время выполнения все операции выделения и освобождения памяти в куче обрабатываются Scudo для всех исполняемых файлов и их библиотечных зависимостей, и процесс прерывается, если обнаруживается повреждение или подозрительное поведение в куче.

Scudo — это программное обеспечение с открытым исходным кодом , являющееся частью проекта compiler-rt от LLVM. Документация доступна по адресу 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 .