HarmonyOS的NDK开发实战(使用ASan检查c/c++代码内存问题)
为追求C/C++的极致性能,编译器和OS(Windows/Linux/Mac)运行框架不会对内存操作进行安全检测。针对该场景,DevEco Studio集成ASan(Address-Sanitizer)为开发者提供面向C/C++的地址越界检测能力,并通过FaultLog展示错误的堆栈详情及导致错误的代码行。
使用约束
如果应用内的任一模块使能ASan,那么entry模块需同时使能ASan。如果entry模块未使能ASan,该应用在启动时将闪退,出现CPP Crash报错。 ASan与TSan不可同时开启。 配置参数 ASAN_OPTIONS:在运行时配置ASan的行为,包括设置检测级别、输出格式、内存错误报告的详细程度等。常用参数请查看表1。
ASAN_OPTIONS支持在app.json5中配置,也支持在Run/Debug Configurations中配置。app.json5的优先级较高,即两种方式都配置后,以app.json5中的配置为准。
在app.json5中配置环境变量 打开AppScope > app.json5文件,添加配置示例如下: { "app": { "appEnvironments": [ { "name": "ASAN_OPTIONS", "value": "log_exe_name=true abort_on_error=0 print_cmdline=true" // 示例仅供参考,具体以实际为准 }, ], ... } }
AddressSanitizer (ASan) 使用指南
使用约束
- 模块配置一致性:如果应用内的任一模块启用了ASan,那么
entry
模块也必须同时启用ASan。否则,应用在启动时将闪退并出现CPP Crash报错。 - 互斥检测:ASan与TSan不可同时开启。
配置参数
ASAN_OPTIONS
可用于在运行时配置ASan的行为,包括设置检测级别、输出格式、内存错误报告的详细程度等。常用参数如下:
参数 | 默认值 | 是否必填 | 含义 |
---|---|---|---|
log_exe_name | true | 是 | 指定内存错误日志中是否包含执行文件的名称。不可修改。 |
log_path | /dev/asanlog/asan.log | 否 | ROM版本小于NEXT.0.0.68时必填,值不可修改;NEXT.0.0.68及以上版本不再需要该参数。 |
abort_on_error | false | 是 | 指定在打印错误报告后调用abort() 或_exit() 。false :打印错误报告后使用_exit() 结束进程。true :打印错误报告后使用abort() 结束进程。 |
strip_path_prefix | - | 否 | 内存错误日志的文件路径中去除所配置的前缀。如:/data/storage/el1 |
detect_stack_use_after_return | false | 否 | 指定是否检查访问已被释放的栈空间。true :检查。false :不检查。 |
halt_on_error | 0 | 否 | 检测内存错误后是否继续运行。0 :继续运行。1 :结束运行。 |
malloc_context_size | - | 否 | 内存错误发生时,显示的调用栈层数。 |
suppressions | "" | 否 | 屏蔽文件名。 |
handle_segv | - | 否 | 检查段错误。 |
handle_sigill | - | 否 | 检查SIGILL信号。 |
quarantine_size_mb | 256 | 否 | 指定检测访问已被释放的栈空间错误的隔离区大小。 |
ASAN_OPTIONS
支持在 app.json5
和 Run/Debug Configurations
中配置。app.json5
的优先级较高,即如果两种方式都配置了,以 app.json5
中的配置为准。
在 app.json5
中配置环境变量
打开 AppScope > app.json5
文件,添加配置示例如下:
{
"app": {
"appEnvironments": [
{
"name": "ASAN_OPTIONS",
"value": "log_exe_name=true abort_on_error=0 print_cmdline=true" // 示例仅供参考,具体以实际为准
},
],
...
}
}
在 Run/Debug Configurations
中配置环境变量
请参考相关配置环境变量的文档进行设置。
使能ASan
可以通过以下两种方式使能ASan:
方式一
- 在运行调试窗口,点击
Diagnostics
,勾选Address Sanitizer
。 - 如果有引用本地库,需在库模块的
build-profile.json5
文件中配置arguments
字段值为-DOHOS_ENABLE_ASAN=ON
,表示以ASan模式编译.so
文件。
方式二
- 修改工程目录下
AppScope/app.json5
,添加ASan
配置开关:"asanEnabled": true
- 设置模块级构建
ASan
插桩。在需要启用了ASan
的模块中,通过添加构建参数开启ASan
检测插桩,在对应模块的模块级build-profile.json5
中添加命令参数:"arguments": "-DOHOS_ENABLE_ASAN=ON"
说明:该参数未配置不会报错,但除包含 malloc
和 free
函数等少数内存错误外,出现其他需要插桩检测的内存错误时,ASan
无法检测到错误。
启用ASan
运行或调试当前应用。当程序出现内存错误时,会弹出 ASan
log信息,点击信息中的链接即可跳转至引起内存错误的代码处。
ASan检测异常码
当前提供的案例在 debug
应用中可产生 ASan
,但在 release
应用中,由于编译构建期间会进行代码优化,可能不会产生异常。
heap-buffer-overflow
背景/原理:访问越界。
错误代码实例:
int heapBufferOverflow() {
char *buffer;
buffer = (char *)malloc(10);
*(buffer + 11) = 'n';
*(buffer + 12) = 'n';
free(buffer);
return buffer[1];
}
影响/报错:导致程序存在安全漏洞,并有崩溃风险。
开启ASan检测后:触发demo中的函数,应用会闪退并报 ASan
,包含字段:AddressSanitizer:heap-buffer-overflow
。
定位思路:
- 如果有工程代码,直接开启
ASan
检测,debug模式运行后复现该错误,点击堆栈中的超链接定位到代码行,能看到错误代码的位置。
堆栈信息示例:
Reason:AddressSanitizer:heap-buffer-overflow
Fault thread info:
==appspawn==17140==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x0060019ca8da at pc 0x005ec33c3250 bp 0x007fe9c392f0 sp 0x007fe9c392e8
WRITE of size 1 at 0x0060019ca8da thread T0 (easandemo_api12)
#0 0x5ec33c324c (/data/storage/el1/bundle/libs/arm64/libentry.so+0x324c) (BuildId: 4f31be36da7e9bc00c9b7bad563e7ccfec4d0347)
#1 0x5ec33c38e0 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x38e0) (BuildId: 4f31be36da7e9bc00c9b7bad563e7ccfec4d0347)
#2 0x7f850b3780 (/system/lib64/platformsdk/libace_napi.z.so+0x33780) (BuildId: 25f88248f530c20439061db9eb4ed152)
0x0060019ca8da is located 0 bytes to the right of 10-byte region [0x0060019ca8d0,0x0060019ca8da)
allocated by thread T0 (easandemo_api12) here:
#0 0x7f82652758 (/system/lib64/libclang_rt.asan.so+0xd2758) (BuildId: aeec20776cc4e8f96db6c6b5603bb49748cc20ff)
#1 0x5ec33c31ec (/data/storage/el1/bundle/libs/arm64/libentry.so+0x31ec) (BuildId: 4f31be36da7e9bc00c9b7bad563e7ccfec4d0347)
#2 0x5ec33c38e0 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x38e0) (BuildId: 4f31be36da7e9bc00c9b7bad563e7ccfec4d0347)
#3 0x7f850b3780 (/system/lib64/platformsdk/libace_napi.z.so+0x33780) (BuildId: 25f88248f530c20439061db9eb4ed152)
#4 0x5ec6a1bcd8 (/system/lib64/module/arkcompiler/stub.an+0x1dccd8)
#5 0x5ec6847f4c (/system/lib64/module/arkcompiler/stub.an+0x8f4c)
对于 release
应用,本地无工程代码,可以使用 AnalyzeStackTrace
功能,提供要解析的 .so
文件,解析结果为源码地址。
堆栈信息示例:
Reason:AddressSanitizer:heap-buffer-overflow
Fault thread info:
==appspawn==17140==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x0060019ca8da at pc 0x005ec33c3250 bp 0x007fe9c392f0 sp 0x007fe9c392e8
WRITE of size 1 at 0x0060019ca8da thread T0 (easandemo_api12)
#0 overflowAndUnderflowOfBuffers() at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:47)
#1 AsanCheck(napi_env__*, napi_callback_info__*) at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:89)
#2 0x7f850b3780 (/system/lib64/platformsdk/libace_napi.z.so+0x33780) (BuildId: 25f88248f530c20439061db9eb4ed152)
0x0060019ca8da is located 0 bytes to the right of 10-byte region [0x0060019ca8d0,0x0060019ca8da)
allocated by thread T0 (easandemo_api12) here:
#0 0x7f82652758 (/system/lib64/libclang_rt.asan.so+0xd2758) (BuildId: aeec20776cc4e8f96db6c6b5603bb49748cc20ff)
#1 overflowAndUnderflowOfBuffers() at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:46)
#2 AsanCheck(napi_env__*, napi_callback_info__*) at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:89)
#3 0x7f850b3780 (/system/lib64/platformsdk/libace_napi.z.so+0x33780) (BuildId: 25f88248f530c20439061db9eb4ed152)
#4 0x5ec6a1bcd8 (/system/lib64/module/arkcompiler/stub.an+0x1dccd8)
#5 0x5ec6847f4c (/system/lib64/module/arkcompiler/stub.an+0x8f4c)
修改方法:注意数组容量不要访问越界。
推荐建议:
- 已知大小的集合注意访问不要越界。
- 对于位置大小的集合,访问前先判断大小。
stack-buffer-underflow
背景/原理:访问越下界。
错误代码实例:
int stackBufferUnderflow() {
int subscript = -1;
char buffer[42];
buffer[subscript] = 42;
return 0;
}
影响/报错:导致程序存在安全漏洞,并有崩溃风险。
开启ASan检测后:触发demo中的函数,应用会闪退并报 ASan
,包含字段:AddressSanitizer:stack-buffer-underflow
。
定位思路:
- 如果有工程代码,直接开启
ASan
检测,debug模式运行后复现该错误,点击堆栈中的超链接定位到代码行,能看到错误代码的位置。
堆栈信息示例:
Reason:AddressSanitizer:stack-buffer-underflow
Fault thread info:
==appspawn==17039==ERROR: AddressSanitizer: stack-buffer-underflow on address 0x007e07c6027f at pc 0x007f1bdc3994 bp 0x007e07c60250 sp 0x007e07c60248
WRITE of size 1 at 0x007e07c6027f thread T0 (easandemo_api12)
#0 0x7f1bdc3990 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x3990) (BuildId: e34349d8024d23ca83c7c7c3b9f69505d2beb3a0)
#1 0x7f1bdc3fa8 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x3fa8) (BuildId: e34349d8024d23ca83c7c7c3b9f69505d2beb3a0)
#2 0x7e838339a8 (/system/lib64/platformsdk/libace_napi.z.so+0x339a8) (BuildId: f48b24ee6f099a2107ef30b4ace050de)
Address 0x007e07c6027f is located in stack of thread T0 (easandemo_api12) at offset 31 in frame
#0 0x7f1bdc3820 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x3820) (BuildId: e34349d8024d23ca83c7c7c3b9f69505d2beb3a0)
对于 release
应用,本地无工程代码,可以使用 AnalyzeStackTrace
功能,提供要解析的 .so
文件,解析结果为源码地址。
堆栈信息示例:
Reason:AddressSanitizer:stack-buffer-underflow
Fault thread info:
==appspawn==17039==ERROR: AddressSanitizer: stack-buffer-underflow on address 0x007e07c6027f at pc 0x007f1bdc3994 bp 0x007e07c60250 sp 0x007e07c60248
WRITE of size 1 at 0x007e07c6027f thread T0 (easandemo_api12)
#0 stackBufferUnderflow() at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:95)
#1 AsanCheck(napi_env__*, napi_callback_info__*) at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:135)
#2 0x7e838339a8 (/system/lib64/platformsdk/libace_napi.z.so+0x339a8) (BuildId: f48b24ee6f099a2107ef30b4ace050de)
Address 0x007e07c6027f is located in stack of thread T0 (easandemo_api12) at offset 31 in frame
#0 stackBufferUnderflow() at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:92)
修改方法:访问索引不应小于下界。
推荐建议:
- 访问索引不应小于下界。
stack-use-after-scope
背景/原理:栈变量在作用域之外被使用。
错误代码实例:
int *gp;
bool b = true;
int stackUseAfterScope() {
if (b) {
int x[5];
gp = x + 1;
}
return *gp;
}
影响/报错:导致程序存在安全漏洞,并有崩溃风险。
开启ASan检测后:触发demo中的函数,应用会闪退并报 ASan
,包含字段:AddressSanitizer:stack-use-after-scope
。
定位思路:
- 如果有工程代码,直接开启
ASan
检测,debug模式运行后复现该错误,点击堆栈中的超链接定位到代码行,能看到错误代码的位置。
堆栈信息示例:
Reason:AddressSanitizer:stack-use-after-scope
Fault thread info:
==appspawn==7494==ERROR: AddressSanitizer: stack-use-after-scope on address 0x007ffa213b44 at pc 0x005ebf0431e4 bp 0x007ffa213b10 sp 0x007ffa213b08
READ of size 4 at 0x007ffa213b44 thread T0 (easandemo_api12)
#0 0x5ebf0431e0 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x31e0) (BuildId: cf28a04a79da128bc344416e8d5f860e3e22f495)
#1 0x5ebf0437f4 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x37f4) (BuildId: cf28a04a79da128bc344416e8d5f860e3e22f495)
#2 0x7f868b3780 (/system/lib64/platformsdk/libace_napi.z.so+0x33780) (BuildId: 25f88248f530c20439061db9eb4ed152)
Address 0x007ffa213b44 is located in stack of thread T0 (easandemo_api12) at offset 36 in frame
#0 0x5ebf043024 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x3024) (BuildId: cf28a04a79da128bc344416e8d5f860e3e22f495)
对于 release
应用,本地无工程代码,可以使用 AnalyzeStackTrace
功能,提供要解析的 .so
文件,解析结果为源码地址。
堆栈信息示例:
Reason:AddressSanitizer:stack-use-after-scope
Fault thread info:
==appspawn==7494==ERROR: AddressSanitizer: stack-use-after-scope on address 0x007ffa213b44 at pc 0x005ebf0431e4 bp 0x007ffa213b10 sp 0x007ffa213b08
READ of size 4 at 0x007ffa213b44 thread T0 (easandemo_api12)
#0 stackUseAfterScope() at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:53)
#1 AsanCheck(napi_env__*, napi_callback_info__*) at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:92)
#2 0x7f868b3780 (/system/lib64/platformsdk/libace_napi.z.so+0x33780) (BuildId: 25f88248f530c20439061db9eb4ed152)
Address 0x007ffa213b44 is located in stack of thread T0 (easandemo_api12) at offset 36 in frame
#0 stackUseAfterScope() at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:48)
修改方法:在作用域内使用该变量。
推荐建议:
- 注意变量的作用域。
attempt-free-nonallocated-memory
背景/原理:尝试释放了非堆对象(non-heap object)或未分配内存。
错误代码实例:
int main() {
int value = 42;
free(&value);
return 0;
}
影响/报错:导致程序存在安全漏洞,并有崩溃风险。
开启ASan检测后:触发demo中的函数,应用会闪退并报 ASan
,包含字段:AddressSanitizer: attempting free on address which was not malloc()-ed
定位思路:
- 如果有工程代码,直接开启
ASan
检测,debug模式运行后复现该错误,点击堆栈中的超链接定位到代码行,能看到错误代码的位置。
堆栈信息示例:
Reason:AddressSanitizer:attempting
Fault thread info:
==appspawn==20382==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x007fd59ae8c0 in thread T0 (easandemo_api12)
#0 0x7f83a92630 (/system/lib64/libclang_rt.asan.so+0xd2630) (BuildId: aeec20776cc4e8f96db6c6b5603bb49748cc20ff)
#1 0x5ec45c3120 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x3120) (BuildId: 743109db136e66f875a7bc47db74a8095758d4ff)
#2 0x5ec45c3720 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x3720) (BuildId: 743109db136e66f875a7bc47db74a8095758d4ff)
#3 0x7f8a2f3780 (/system/lib64/platformsdk/libace_napi.z.so+0x33780) (BuildId: 25f88248f530c20439061db9eb4ed152)
Address 0x007fd59ae8c0 is located in stack of thread T0 (easandemo_api12) at offset 32 in frame
#0 0x5ec45c2fbc (/data/storage/el1/bundle/libs/arm64/libentry.so+0x2fbc) (BuildId: 743109db136e66f875a7bc47db74a8095758d4ff)
对于 release
应用,本地无工程代码,可以使用 AnalyzeStackTrace
功能,提供要解析的 .so
文件,解析结果为源码地址。
堆栈信息示例:
Reason:AddressSanitizer:attempting
Fault thread info:
==appspawn==20382==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x007fd59ae8c0 in thread T0 (easandemo_api12)
#0 0x7f83a92630 (/system/lib64/libclang_rt.asan.so+0xd2630) (BuildId: aeec20776cc4e8f96db6c6b5603bb49748cc20ff)
#1 doubleFree() at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:46)
#2 AsanCheck(napi_env__*, napi_callback_info__*) at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:86)
#3 0x7f8a2f3780 (/system/lib64/platformsdk/libace_napi.z.so+0x33780) (BuildId: 25f88248f530c20439061db9eb4ed152)
Address 0x007fd59ae8c0 is located in stack of thread T0 (easandemo_api12) at offset 32 in frame
#0 doubleFree() at (D:/TestProjects/ReleaseASanDemo_API12/ReleaseASanDemo_API12/entry/src/main/cpp/napi_init.cpp:44)
修改方法:不要对非堆对象或未分配的内存使用 free
函数。
推荐建议:
- 不要对非堆对象或未分配的内存使用
free
函数。
double-free
背景/原理:重复释放内存。
其他资源:
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/yyz_1987/article/details/143693422
通过以上详细配置和使用说明,开发者可以更有效地利用ASan来检测和定位C/C++代码中的内存错误,提高代码的稳定性和安全性。
- 3回答
- 3粉丝
- 4关注
- HarmonyOS Next应用开发实战:ArkWeb使用介绍及使用举例
- HarmonyOS Next应用开发实战:广告的使用介绍及避坑指南
- 求助:关于元服务中使用preferences的相关问题
- [HarmonyOS]解决HMRouter路由地址无法抽取的问题
- HarmonyOS NEXT应用开发指南:开屏广告的使用
- 【HarmonyOS Next开发】Navigation使用
- 【HarmonyOS Next开发】:ListItemGroup使用
- 【HarmonyOS Next开发】Tabs使用封装
- 鸿蒙Flutter实战:08-如何调试代码
- HarmonyOS NEXT 应用开发实战:音乐播放器的完整实现
- HarmonyOS应用开发实战:半天实现知乎日报项目( 五、组件导航Navigation使用详解)
- 鸿蒙Flutter实战:06-使用ArkTs开发Flutter鸿蒙插件
- 鸿蒙Flutter实战:12-使用模拟器开发调试
- 鸿蒙Flutter实战:10-常见问题集合
- OpenHarmony: 优化import依赖树较大的问题