HarmonyOS的NDK开发实战(使用ASan检查c/c++代码内存问题)

2024-12-24 12:29:14
11次阅读
0个评论

为追求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) 使用指南

使用约束

  1. 模块配置一致性:如果应用内的任一模块启用了ASan,那么entry模块也必须同时启用ASan。否则,应用在启动时将闪退并出现CPP Crash报错。
  2. 互斥检测: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.json5Run/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:

方式一
  1. 在运行调试窗口,点击 Diagnostics,勾选 Address Sanitizer
  2. 如果有引用本地库,需在库模块的 build-profile.json5 文件中配置 arguments 字段值为 -DOHOS_ENABLE_ASAN=ON,表示以ASan模式编译 .so 文件。
方式二
  1. 修改工程目录下 AppScope/app.json5,添加 ASan 配置开关:
    "asanEnabled": true
    
  2. 设置模块级构建 ASan 插桩。在需要启用了 ASan 的模块中,通过添加构建参数开启 ASan 检测插桩,在对应模块的模块级 build-profile.json5 中添加命令参数:
    "arguments": "-DOHOS_ENABLE_ASAN=ON"
    

说明:该参数未配置不会报错,但除包含 mallocfree 函数等少数内存错误外,出现其他需要插桩检测的内存错误时,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++代码中的内存错误,提高代码的稳定性和安全性。

收藏00

登录 后评论。没有帐号? 注册 一个。