Scudo

Scudo 是一个动态用户模式内存分配器或分配器,旨在抵御与堆相关的漏洞(例如基于堆的缓冲区溢出释放后使用双重释放),同时保持性能。它提供标准的 C 分配和释放原语(例如malloc和 free)以及 C++ 原语(例如 new 和 delete)。

Scudo 比AddressSanitizer (ASan)等成熟的内存错误检测器更像是一种缓解措施。

自 Android 11 版本以来,scudo 用于所有本机代码(低内存设备上仍使用 jemalloc 除外)。在运行时,所有本机堆分配和释放均由 Scudo 为所有可执行文件及其库依赖项提供服务,如果在堆中检测到损坏或可疑行为,则进程将中止。

Scudo 是开源的,是 LLVM 编译器-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用于延迟块的实际释放的隔离区的大小(以 KB 为单位)。较低的值可能会减少内存使用量,但会降低缓解措施的有效性;负值会回退到默认值。将此值和ThreadLocalQuarantineSizeKb设置为零会完全禁用隔离。
QuarantineChunksUpToSize 2048 512可以隔离块的最大大小(以字节为单位)。
ThreadLocalQuarantineSizeKb 64 16用于卸载全局隔离的每线程缓存的大小(以 KB 为单位)。较低的值可能会减少内存使用量,但可能会增加全局隔离区的争用。将此值和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()

验证

目前,还没有专门针对 Scudo 的 CTS 测试。相反,请确保在为给定二进制文件启用或不启用 Scudo 的情况下通过 CTS 测试,以验证它不会影响设备。

故障排除

如果检测到不可恢复的问题,分配器会向标准错误描述符显示一条错误消息,然后终止该进程。导致终止的堆栈跟踪将添加到系统日志中。输出通常以Scudo ERROR:开头,后跟问题的简短摘要以及任何指针。

以下是当前错误消息及其潜在原因的列表:

  • corrupted chunk header :块头的校验和验证失败。这可能是由于以下两种情况之一造成的:标头被覆盖(部分或全部),或者传递给函数的指针不是块。
  • race on chunk header :两个不同的线程试图同时操作同一个头。这通常是在对该块执行操作时出现竞争条件或普遍缺乏锁定的症状。
  • invalid chunk state :块未处于给定操作的预期状态,例如,尝试释放它时未分配它,或者尝试回收它时未隔离它。双重释放是此错误的典型原因。
  • misaligned pointer :强制执行基本对齐要求:32 位平台上为 8 字节,64 位平台上为 16 字节。如果传递给我们的函数的指针不适合这些,则传递给其中一个函数的指针就会失去对齐。
  • allocation type mismatch :启用此选项时,在块上调用的释放函数必须与调用来分配它的函数类型相匹配。这种类型的不匹配可能会带来安全问题。
  • invalid sized delete :当使用 C++14 大小的删除运算符并启用可选检查时,释放块时传递的大小与分配块时请求的大小之间存在不匹配。这通常是编译器问题或正在释放的对象的类型混淆
  • RSS limit exhausted :已超出可选指定的最大 RSS。

如果您正在调试操作系统本身的崩溃,则可以使用HWASan OS 构建。如果您正在调试应用程序中的崩溃,也可以使用HWASan 应用程序构建