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 .