C 和 C++ 中的未初始化内存是可靠性问题、内存安全 bug 和信息泄露的常见原因。为了避免这些问题,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。
零初始化内核内存
根据 CDD 的强烈建议,GKI 内核的内核堆栈和堆均采用零初始化。
在堆栈初始化方面,GKI 使用 CONFIG_INIT_STACK_ALL_ZERO
配置,这将使用 -ftrivial-auto-var-init=zero
编译器标志来构建内核。
在堆初始化方面,GKI 使用 CONFIG_INIT_ON_ALLOC_DEFAULT_ON
,这会让所有页面堆、SLAB 和 SLUB 分配在创建时进行零初始化。此选项的效果类似于将 init_on_alloc=1
作为内核启动选项传递。
错误报告
我们的工具会生成富含分析洞见的 bug 报告,其中包含可帮助调试的更多信息。更多的分配和取消分配堆栈轨迹可帮助更好地了解给定分配的生命周期,并更快确定发生内存安全 bug 的根本原因。
在开发过程中,供应商应通过检查 /data/tombstones
和 logcat
是否发生原生代码崩溃来监控是否存在 bug。如需详细了解如何调试 Android 原生代码,请参阅此处的相关信息。