HarmonyOS 鸿蒙Next的NDK开发实战(使用ASan检查c/c++代码内存问题)
HarmonyOS 鸿蒙Next的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
背景/原理:重复释放内存。
其他资源:
- 知乎专栏
- 文档中心 — ASan检测
- 华为开发者问答专区 — 使用Snapshot Insight分析ArkTS内存问题
- #HarmonyOS NEXT体验官# 鸿蒙NDK开发入门
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/yyz_1987/article/details/143693422
通过以上详细配置和使用说明,开发者可以更有效地利用ASan来检测和定位C/C++代码中的内存错误,提高代码的稳定性和安全性。
更多关于HarmonyOS 鸿蒙Next的NDK开发实战(使用ASan检查c/c++代码内存问题)的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于HarmonyOS 鸿蒙Next的NDK开发实战(使用ASan检查c/c++代码内存问题)的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS 鸿蒙Next的NDK开发实战中,使用ASan(AddressSanitizer)检查C/C++代码内存问题是一项重要技能。ASan是一种内存错误检测工具,能够检测多种内存错误,包括越界访问、使用已释放的内存、双重释放等。
要在HarmonyOS项目中启用ASan,首先需要在编译配置中启用ASan支持。这通常涉及修改build.gradle
文件或CMakeLists.txt文件,添加相应的编译选项。例如,在CMakeLists.txt中,可以通过添加-fsanitize=address
编译器标志来启用ASan。
启用ASan后,编译项目时,编译器会插入额外的检查代码,以检测运行时内存错误。当程序运行时,如果检测到内存错误,ASan会输出详细的错误信息,包括错误类型、错误发生的文件和行号等,帮助开发者快速定位问题。
需要注意的是,ASan会增加程序的内存占用和运行时间,因此在生产环境中使用时需要谨慎。此外,由于ASan依赖于特定的编译器和运行时库,因此在不同平台上的支持情况可能有所不同。
如果在HarmonyOS鸿蒙Next的NDK开发中使用ASan时遇到问题,建议检查编译配置和ASan的使用方式是否正确。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html。