零初始化的内存

C 和 C++ 中未初始化的内存是导致可靠性问题、内存安全漏洞和信息泄露的常见原因。为了避免这些问题,Android 会尽可能多地初始化内存。

零初始化的用户空间内存

自 Android 12 起,堆栈内存会在所有平台原生代码(包括 JNI)中进行零初始化,堆内存会在所有平台原生进程(例如 netd)中进行零初始化,但在 zygote 或应用中则不会。

强烈建议使用 NDK 构建的第一方和第三方应用使用 -ftrivial-auto-var-init=zero 编译器标志来零初始化其堆栈局部变量。编译器会优化掉任何不必要的归零操作。例如,当显式初始化局部变量时(例如,int x = 123; 变量 x 只会初始化一次)。如果程序在性能热点中有一个大型堆栈缓冲区,开发者可以使用编译器属性禁用初始化

__attribute__((__uninitialized__)) char buf[BUFSIZ];

应用还可以选择使用 android:nativeHeapZeroInitialized 清单属性来选择堆零初始化。或者,堆零初始化可以在运行时通过以下方式控制:

int mallopt(M_BIONIC_ZERO_INIT, level)

其中 level 为 0 或 1。

零初始化的内核内存

GKI 内核的内核堆栈和堆已进行零初始化,CDD 强烈建议这样做。

对于堆栈初始化,GKI 使用 CONFIG_INIT_STACK_ALL_ZERO 配置,这会导致使用 -ftrivial-auto-var-init=zero 编译器标志构建内核。对于堆初始化,GKI 使用 CONFIG_INIT_ON_ALLOC_DEFAULT_ON,这使得所有页面堆、SLAB 和 SLUB 分配在创建时都进行零初始化。此选项实际上类似于将 init_on_alloc=1 作为内核启动时选项传递。

错误报告

我们的工具生成有见地的错误报告,其中包含有助于调试的其他信息。额外的分配和取消分配堆栈跟踪有助于更好地了解给定分配的生命周期,并更快地找到内存安全错误的根本原因。

Example of a bug report generated
  by memory safety tool
图 1:内存安全工具生成的错误报告

在开发期间,供应商应通过检查 /data/tombstoneslogcat 以查找原生崩溃来监控错误的存在。有关调试 Android 原生代码的更多信息,请参阅此处的信息