鸿蒙5.0 APP性能优化实战——使用HWAsan检测内存错误
HWASan是Hardware-Assisted Address Sanitizer的简称,它是Clang LLVM提供的一套内存错误检测系统,用来检测C/C++中常见的内存访问错误,相比之前的Asan(Address Sanitizer),它在性能、内存上有不小提升,依赖于编译器的Address Tagging特性,该特性允许应用程序自定义数据存储到虚拟地址的最高8位,当CPU操作该虚拟地址时会
往期推文全新看点(文中附带全新鸿蒙5.0全栈学习笔录)
✏️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
✏️ 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
✏️ 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
✏️ 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
✏️ 记录一场鸿蒙开发岗位面试经历~
✏️ 持续更新中……
原理概述
HWASan是Hardware-Assisted Address Sanitizer的简称,它是Clang LLVM提供的一套内存错误检测系统,用来检测C/C++中常见的内存访问错误,相比之前的Asan(Address Sanitizer),它在性能、内存上有不小提升,依赖于编译器的Address Tagging特性,该特性允许应用程序自定义数据存储到虚拟地址的最高8位,当CPU操作该虚拟地址时会自动忽略它。HWASan工具检测地址越界问题的原理如下,
- 将整个虚拟内存区间按照16:1的比例,划分为user memory和shadow memory;同时,无论是堆上、栈上还是全局对象,其内存起始地址都按照16字节对齐,即保证每16字节的user memory都能映射到1字节的shadow memory;
- 分配对象的时候,随机分配一个8位的随机tag标记到该对象的虚拟地址最高8位,同时该tag也会保存到其映射的shadow memory中;
- 编译器在每个内存地址的load/store之前都会插入检查指令,用于确认操作地址的最高8位保存的tag与其映射的shadow memory中的tag值是否一致;
- 对象回收后也会重新分配一个随机值,保存到其映射的shadow memory中,当出现内存越界行为时,就会检测到tag值不一致的异常;
注意,当分配的对象小于16字节时,多余的内存不会再分配给其它对象,此时shadow memory中保存的是对象所占内存的实际字节数,而tag值则保存在16字节的最后一个字节里面。
常见的HWASan异常检测类型有stack-buffer-overflow/underflow,stack-use-after-return,heap-buffer-overflow等。
功能介绍
HWASan能检测到ASan所能检测到的同一系列错误:
- 堆栈和堆缓冲区上溢/下溢。
- 释放之后的堆使用情况。
- 重复释放/错误释放。
和ASan相比,HWASan具有以下优点:
- HWASan不需要安全区来检测buffer overflow,既极大地降低了工具对于内存的消耗,也不会出现ASan中某些overflow检测不到的情况。
- HWASan不需要隔离区来检测UseAfterFree,因此不会出现ASan中某些UseAfterFree检测不到的情况。
- 此外,HWASan还可以检测返回之后的堆栈使用情况。
使用约束
- ROM版本5.0.0.107及以上支持。
- ASan、TSan、HWASan、UBsan不能同时开启,四个只能开启其中一个。
配置HWASan
可通过方式一和方式二使能HWAsan,每种方式分为DevEco Studio场景和流水线场景。
方式一
DevEco Studio****场景
点击Run > Edit Configurations >**Diagnostics,勾选Hardware-Assisted Address Sanitizer**开启检测。

流水线场景
- 修改工程目录下的AppScope/app.json5文件,添加HWASan配置开关。
"hwasanEnabled": true

- 在hvigorw命令后加上**-p ****ohos-enable-hwasan=true**的选项,执行hvigorw命令,
hvigorw [taskNames...] -p ohos-enable-hwasan=true <options>
方式二
DevEco Studio场景
- 修改工程目录下的AppScope/app.json5文件,添加HWASan配置开关。
"hwasanEnabled": true

2. 在需要使能HWASan的模块中,通过添加构建参数开启HWASan检测插桩,在对应模块的模块级build-profile.json5中添加命令参数:
"arguments": "-DOHOS_ENABLE_HWASAN=ON"

流水线场景
在AppScope/app.json5和模块build-profile.json5配置对应HWASan项后,可直接执行hvigorw命令
hvigorw [taskNames...] <options>
说明
如果按方式一勾选以后,配置app.json5中的为false,HWASan仍生效
运行HWASan
- 运行或调试当前应用。
2. 当程序出现内存错误时,弹出HWASan log信息,点击信息中的链接即可跳转至引起内存错误的代码处。

HWasan异常检测类型
stack tag-mismatch
背景
"stack tag-mismatch"在HWASan中指的是栈内存标签不匹配错误。这种错误通常发生在访问栈内存时,指针携带的标签与栈内存中存储的标签不一致,触发其异常检测码字的异常类型有:stack-buffer-overflow/underflow、stack-use-after-return。
错误代码实例
// stack-buffer-overflow
int stackBufferOverflow() {
int subscript = 43;
char buffer[42];
buffer[subscript] = 42;
return 0;
}
// stack-buffer-underflow
int stackBufferUnderflow() {
int subscript = -1;
char buffer[42];
buffer[subscript] = 42;
return 0;
}
// stack-use-after-return
int *ptr;
__attribute__((noinline))
void FunctionThatEscapesLocalObject() {
int local[100];
ptr = &local[0];
}
int main(int argc, char **argv) {
FunctionThatEscapesLocalObject();
return ptr[argc];
}
影响
导致程序存在安全漏洞,并有崩溃风险。
开启ASan检测后,触发demo中的函数,应用闪退报HWASAN,包含字段:
HWAddressSanitizer: tag-mismatch on address
Cause: stack tag-mismatch
定位思路
如果有工程代码,直接开启HWAsan检测,debug模式运行后复现该错误,可以触发WAsan,直接点击堆栈中的超链接定位到代码行,能看到错误代码的位置。
Reason:HWASAN
==appspawn==61390==ERROR: HWAddressSanitizer: tag-mismatch on address 0x007eb11cc05f at pc 0x005acf446438
WRITE of size 1 at 0x007eb11cc05f tags: f1/00 (ptr/mem) in thread T0
#0 0x5acf446438 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x6438) (BuildId: 4b0b8d2189a7eb99fff81c6bc8889dfefd4af4a1)
#1 0x5acf446c08 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x6c08) (BuildId: 4b0b8d2189a7eb99fff81c6bc8889dfefd4af4a1)
#2 0x5ab397cdc8 (/system/lib64/platformsdk/libace_napi.z.so+0x3cdc8) (BuildId: dc293a22b36a4db17dc689ff9413b64e)
Cause: stack tag-mismatch
Address 0x007eb11cc05f is located in stack of thread T0
Thread: T0 0x005b00002000 stack: [0x007eb09d2000,0x007eb11d1000) sz: 8384512 tls: [0x005aacd0fa98,0x005aacd10279)
Previously allocated frames:
record_addr:0x5ab053c788 record:0xcc0a005acf4463c0 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x63c0) (BuildId: 4b0b8d2189a7eb99fff81c6bc8889dfefd4af4a1)
record_addr:0x5ab053c780 record:0xcc1a005acf446a68 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x6a68) (BuildId: 4b0b8d2189a7eb99fff81c6bc8889dfefd4af4a1)
record_addr:0x5ab053c778 record:0xcb81005acf4866ac (/data/storage/el1/bundle/libs/arm64/libmainpage.so+0x66ac) (BuildId: 10ffa0d04cbc27e12a59f658b5932674185d63ee)
record_addr:0x5ab053c770 record:0xcc87005acf4465b4 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x65b4) (BuildId: 4b0b8d2189a7eb99fff81c6bc8889dfefd4af4a1)
修改方法
stack-buffer-overflow/underflow**:**访问索引要落在给定的范围内
stack-use-after-return:在作用域内使用局部变量
推荐建议
stack-buffer-overflow/underflow:访问索引不要超过给定的上界/下界
stack-use-return: 如果需要在函数返回后继续使用某些数据,考虑将它们存储在静态或全局变量中
heap-buffer-overflow
背景
访问堆内存越界(上下界),触发其异常检测码字的异常类型有:heap-buffer-overflow、heap-buffer-underflow
错误代码实例
// heap-buffer-overflow
void heapBufferOverflow() {
char *buffer;
buffer = (char *)malloc(10);
*(buffer + 11) = 'n';
*(buffer + 12) = 'n';
free(buffer);
}
// heap-buffer-underflow
void heapBufferUnderflow() {
char *buffer;
buffer = (char *)malloc(10);
*(buffer - 11) = 'n';
*(buffer - 12) = 'n';
free(buffer);
}
影响
导致程序存在安全漏洞,并有崩溃风险。
开启HWASan检测后,触发demo中的函数,应用闪退报HWASan,包含字段:
HWAddressSanitizer: tag-mismatch
Cause: heap-buffer-overflow
定位思路
如果有工程代码,直接开启HWASan检测,debug模式运行后复现该错误,可以触发HWASan,直接点击堆栈中的超链接定位到代码行,能看到错误代码的位置。
Reason:HWASAN
==appspawn==8344==ERROR: HWAddressSanitizer: tag-mismatch on address 0x000100760332 at pc 0x005adb286570
WRITE of size 1 at 0x000100760332 tags: cb/08(44) (ptr/mem) in thread T0
#0 0x5adb286570 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x6570) (BuildId: 4724eac5a66f4994a03c023a9958da3897de5f16)
#1 0x5adb286c8c (/data/storage/el1/bundle/libs/arm64/libentry.so+0x6c8c) (BuildId: 4724eac5a66f4994a03c023a9958da3897de5f16)
#2 0x5abd63cdc8 (/system/lib64/platformsdk/libace_napi.z.so+0x3cdc8) (BuildId: dc293a22b36a4db17dc689ff9413b64e)
[0x000100760320,0x000100760340) is a small allocated heap chunk; size: 32 offset: 18
Cause: heap-buffer-overflow
0x000100760332 is located 110 bytes to the left of 10-byte region [0x0001007603a0,0x0001007603aa)
allocated here:
#0 0x5a394625ec (/system/lib64/libclang_rt.hwasan.so+0x225ec) (BuildId: 2b2455ae77181bfdfbfa9561b4e6e3ea376ec93b)
#1 0x5adb286548 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x6548) (BuildId: 4724eac5a66f4994a03c023a9958da3897de5f16)
#2 0x5adb286c8c (/data/storage/el1/bundle/libs/arm64/libentry.so+0x6c8c) (BuildId: 4724eac5a66f4994a03c023a9958da3897de5f16)
#3 0x5abd63cdc8 (/system/lib64/platformsdk/libace_napi.z.so+0x3cdc8) (BuildId: dc293a22b36a4db17dc689ff9413b64e)
#4 0x5ad4373b6c (/system/lib64/module/arkcompiler/stub.an+0x3f3b6c)
#5 0x5ad3f8be8c (/system/lib64/module/arkcompiler/stub.an+0xbe8c)
修改方法
注意数组长度,不能越界
heap-buffer-overflow: 数组访问位置不要越上界
heap-buffer-underflow: 访问数组位置不要越下界
推荐建议
已知大小的数组注意访问不要越界,访问已知大小数组前先判断访问位置是否落在边界外
Use-after-free
背景
触发其异常检测码字的异常类型有:heap-use-after-free、double-free
heap-use-after-free: 当指针指向的内存被释放后,仍然通过该指针访问已经被释放的内存,就会触发heap-use-after-free
double-free: 重复释放内存
错误代码实例
// heap-use-after-free
int useAfterFree(int argc) {
int *array = new int[100];
delete[] array;
return array[argc];
}
// double free
void doubleFree() {
char *p = (char *)malloc(32 * sizeof(char));
free(p);
free(p);
}
影响
导致程序存在安全漏洞,并有崩溃风险。
开启HWAsan检测后,触发demo中的函数,应用闪退报HWAsan,包含字段:
HWAddressSanitizer: tag-mismatch
Cause: use-after-free
定位思路
如果有工程代码,直接开启ASan检测,debug模式运行后复现该错误,可以触发ASan,直接点击堆栈中的超链接定位到代码行,能看到错误代码的位置。
Reason:HWASAN
==appspawn==10741==ERROR: HWAddressSanitizer: tag-mismatch on address 0x000d00036a68 at pc 0x005ab24864c4
READ of size 4 at 0x000d00036a68 tags: ae/6e (ptr/mem) in thread T0
#0 0x5ab24864c4 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x64c4) (BuildId: e073772c81ab52894a21e4190a263680198f3e9c)
#1 0x5ab2486c84 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x6c84) (BuildId: e073772c81ab52894a21e4190a263680198f3e9c)
#2 0x5a96b7cdc8 (/system/lib64/platformsdk/libace_napi.z.so+0x3cdc8) (BuildId: dc293a22b36a4db17dc689ff9413b64e)
[0x000d00036a60,0x000d00036c00) is a small unallocated heap chunk; size: 416 offset: 8
Cause: use-after-free
0x000d00036a68 is located 8 bytes inside of 400-byte region [0x000d00036a60,0x000d00036bf0)
freed by thread T0 here:
#0 0x5a90a6844c (/system/lib64/libclang_rt.hwasan.so+0x2844c) (BuildId: 2b2455ae77181bfdfbfa9561b4e6e3ea376ec93b)
#1 0x5ab2486494 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x6494) (BuildId: e073772c81ab52894a21e4190a263680198f3e9c)
#2 0x5ab2486c84 (/data/storage/el1/bundle/libs/arm64/libentry.so+0x6c84) (BuildId: e073772c81ab52894a21e4190a263680198f3e9c)
#3 0x5a96b7cdc8 (/system/lib64/platformsdk/libace_napi.z.so+0x3cdc8) (BuildId: dc293a22b36a4db17dc689ff9413b64e)
#4 0x5aab6b3b6c (/system/lib64/module/arkcompiler/stub.an+0x3f3b6c)
#5 0x5aab2cbe8c (/system/lib64/module/arkcompiler/stub.an+0xbe8c)
修改方法
heap-use-after-free: 已经释放的指针不要再使用,将指针设置为NULL/nullptr。
double-free:已经释放一次的指针,不要再重复释放。
推荐建议
heap-use-after-free: 使用智能指针,或实现一个free()函数的替代版本或者 delete析构器来保证指针的重置。
double-free: 变量定义声明时初始化为NULL,释放内存后也应立即将变量重置为NULL,这样每次释放之前都可以通过判断变量是否为NULL来判断是否可以释放。
更多推荐


所有评论(0)