1# 用户态内存调测 2## 基本概念 3 4 5Debug版本的musl-libc库为用户提供内存泄漏检测、堆内存统计、踩内存分析以及backtrace功能等维测手段,可以提高用户态内存相关问题的定位效率。 6 7 8采用了对malloc/free接口进行插桩,保存关键节点信息,然后程序在申请和释放内存时进行内存节点完整性校验,最后在程序结束时通过统计节点信息得到内存统计信息并根据统计信息判断内存是否泄漏的设计思想。 9 10## 运行机制 11 12 13### 内存泄漏检查 14 15对于每个进程,内存调测模块维护了128个链表(当前系统的线程最大数量为128个),每个链表的索引为线程ID。 16 17申请内存时:保存关键信息到内存节点控制块,根据当前线程ID将内存节点控制块挂到对应链表; 18 19释放内存时:根据需要释放的内存地址匹配内存节点控制块并将该控制块删除。 20 21 **图1** 堆内存节点信息链表 22 23 ![zh-cn_image_0000001165890158](figures/zh-cn_image_0000001165890158.png) 24 25申请内存时,返回地址会被保存到LR寄存器中。进程运行过程中,系统会在内存节点控制块中添加疑似泄漏点对应的lr等信息。如下图所示: 26 27 **图2** 堆内存节点信息 28 29 ![zh-cn_image_0000001165890518](figures/zh-cn_image_0000001165890518.png) 30 31其中,TID表示线程ID;PID表示进程ID;ptr表示申请的内存地址;size表示申请的内存大小;lr[n]表示函数调用栈地址,变量n的大小可以根据具体场景的需要进行配置。 32 33释放内存时,将free等接口的入参指针与node的ptr字段进行匹配,如果相同则删除该内存节点控制块信息。 34 35用户通过串口或文件等方式,将各个进程内存调测信息导出,利用addr2line工具将导出的信息转换成导致内存泄漏的代码行,便可以解决内存泄露问题。 36 37 **图3** 泄漏点代码行定位流程 38 39 ![zh-cn_image_0000001165730464](figures/zh-cn_image_0000001165730464.png) 40 41 42### 堆内存统计 43 44用户态线程堆内存使用统计具有一定的实际意义,统计线程申请的堆内存占比,为用户程序的内存使用优化提供数据支持。用户态堆内存统计模块主要涉及的接口为malloc和free。如上图所示,每个进程维护128个链表,链表索引即线程ID,申请内存时系统将ptr和size信息记录在内存节点控制块中并将节点控制块挂在以线程ID为头信息的链表上,堆内存释放时根据ptr从对应的链表上移除相应的堆内存块信息;同时计算出当前线程所持有的堆内存总的使用量,并更新当前进程的堆内存使用量和堆内存使用峰值。 45 46 47### 内存完整性检查 48 49- 使用malloc申请内存(小于等于0x1c000 bytes时通过堆分配算法分配) 50 用户程序申请堆内存时,在堆内存节点处添加校验值等信息,如果校验值异常,则很有可能是前一块堆内存使用越界导致的(目前无法识别校验值被野指针破坏的场景)。在内存申请、释放时校验内存节点校验值的正确性,若内存节点被破坏,校验失败时则输出tid、pid及当前被踩节点前一块堆内存申请时保存的调用栈信息,通过addr2line工具可获得具体的代码行信息,辅助用户解决问题。 51 52 **图4** node节点头信息添加校验值 53 54 ![zh-cn_image_0000001211449151](figures/zh-cn_image_0000001211449151.png) 55 56 free堆内存时,不会立即把该内存块释放掉,而是在内存中写入魔术数字0xFE,并放到free队列中(保证在一定时间内不会再被malloc函数分配),当有野指针或use-after-free的情况对该内存进行读取的操作时,能够发现数据异常,但是对于写操作则无法判断出来。 57 58 **图5** free流程图 59 60 ![zh-cn_image_0000001165890904](figures/zh-cn_image_0000001165890904.png) 61 62- 使用malloc申请内存(大于0x1c000 bytes时通过mmap申请) 63 当malloc通过mmap申请大块内存时,在返回给用户使用的内存区间头和尾分别多申请一个页,一个页PAGE_SIZE当前为0x1000,这两个页分别通过mprotect接口设置权限为PROT_NONE(无可读可写权限),可以有效防止内存越界读写问题:越界读写数据时由于无读写权限而导致用户程序异常,根据异常调用栈信息可找到相应的代码逻辑。 64 65 **图6** malloc通过mmap机制申请内存的内存布局 66 67 ![zh-cn_image_0000001211130993](figures/zh-cn_image_0000001211130993.png) 68 69### 使用指导 70#### 接口说明 71 72 73 **表1** 内存调测功能 74 75| 接口名 | 描述 | 76| -------- | -------- | 77| mem_check_init | 初始化内存检测模块。 | 78| watch_mem | 获取线程级堆内存使用信息。 | 79| check_leak | 检查是否有堆内存泄漏。 | 80| check_heap_integrity | 检查堆内存的完整性。 | 81| backtrace | 获取调用栈地址信息。 | 82| backtrace_symbols | 根据地址信息获取符号信息。 | 83| print_trace | 输出函数调用栈信息。 | 84 85 86 **表2** 调用栈回溯功能 87 88| 接口名 | 描述 | 89| -------- | -------- | 90| backtrace | 获取调用栈地址信息。 | 91| backtrace_symbols | 根据地址信息获取符号信息。 | 92| print_trace | 输出函数调用栈信息。 | 93 94### 使用说明 95 96 97编译OpenHarmony工程时默认编译的是debug版本,即libc库已经集成内存调测相关的接口实现,用户可以根据具体需要决定是否使能内存调测功能。 98 99 100堆内存调测功能提供两种方式供用户使用:接口调用及命令行参数。 101 102 103- 接口调用:优点是可以较精确的检查某一段代码逻辑的堆内存相关信息,缺点是需要修改用户代码。 104 105- 命令行参数:优点是无需修改用户代码,缺点是无法精确的校验某一段逻辑的堆内存信息。 106 107 108> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 109> 内存调测功能使能后,进程退出时会默认进行一次堆内存泄漏和堆内存完整性检查。内存调测功能未使能时,堆内存统计、堆内存泄漏检查、堆内存完整性校验功能不会开启,调用相关调测接口无响应。 110 111 112 113 114#### 接口调用方式 115 116 117##### 示例代码 118 119代码功能:显式调用调测模块的相关接口对用户代码进行内存校验。 120 121 122```c 123#include <pthread.h> 124#include <stdlib.h> 125#include <stdio.h> 126#include <debug.h> // 包含提供内存调测接口声明的头文件 127 128#define MALLOC_LEAK_SIZE 0x300 129 130void func(void) 131{ 132 char *ptr = malloc(MALLOC_LEAK_SIZE); 133 memset(ptr, '3', MALLOC_LEAK_SIZE); 134} 135 136int main() 137{ 138 mem_check_init(NULL); // 通过串口输出内存调测信息,必须在用户程序第一次申请堆内存之前调用(一般在main函数入口调用),否则调测信息不准确。 139 // mem_check_init("/storage/mem_debug.txt"); // 内存调测信息输出到/storage/mem_debug.txt文件中,如果该文件创建失败,则信息通过串口输出。 140 char *ptr = malloc(MALLOC_LEAK_SIZE); 141 memset(ptr, '1', MALLOC_LEAK_SIZE); 142 143 watch_mem(); // 在当前代码逻辑处查看线程级内存统计信息。 144 func(); 145 check_heap_integrity(); // 检查堆内存节点完整性。 146 check_leak(); // 在当前代码逻辑处检查堆内存是否泄漏(一般在程序退出之前校验比较准确,若在malloc和free调用逻辑之间做校验,则结果不准确)。 147 return 0; 148} 149``` 150 151 152##### 编译 153 154 155``` 156$ clang -o mem_check mem_check.c -funwind-tables -rdynamic -g -mfloat-abi=softfp -mcpu=cortex-a7 -mfpu=neon-vfpv4 -target arm-liteos --sysroot=/home/<user-name>/directory/out/hispark_taurus/ipcamera_hispark_taurus/sysroot $(clang -mfloat-abi=softfp -mcpu=cortex-a7 -mfpu=neon-vfpv4 -target arm-liteos -print-file-name=libunwind.a) 157``` 158 159 160> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 161> - 本编译示例基于将编译器的路径写入环境变量中,即.bashrc文件中。 162> 163> - 编译用户程序及所需的lib库时,需要添加编译选项-funwind-tables,-rdynamic,-g,用于栈回溯。 164> 165> - -mfloat-abi=softfp,-mcpu=cortex-a7,-mfpu=neon-vfpv4编译选项用于指定具体的芯片架构、浮点数计算优化、fpu,与具体的libc库使用的编译选项保持一致,否则链接时可能出现找不到libc库文件。 166> 167> - -target arm-liteos用于指定编译器相关库文件路径。 168> 169> - --sysroot=/home/<user-name>/directory/out/hispark_taurus/ipcamera_hispark_taurus/sysroot用于指定编译器库文件搜索根目录,假设OpenHarmony工程代码存放路径为/home/<user-name>/directory。其中out/hispark_taurus/ipcamera_hispark_taurus路径为在编译时,hb set命令指定的具体产品,本示例选择的是ipcamera_hispark_taurus产品。 170> 171> - $(clang -mfloat-abi=softfp -mcpu=cortex-a7 -mfpu=neon-vfpv4 -target arm-liteos -print-file-name=libunwind.a)用于指定相应的unwind库的路径。 172 173 174##### 调测信息 175 176 177``` 178OHOS # ./mem_check 179OHOS # 180==PID:4== Heap memory statistics(bytes): // 堆内存统计信息 181 [Check point]: // check点调用栈 182 #00: <main+0x38>[0x86c] -> mem_check 183 #01: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so 184 185 [TID: 18, Used: 0x320] // 18号线程堆内存占用,当前进程仅一个线程 186 187==PID:4== Total heap: 0x320 byte(s), Peak: 0x320 byte(s) 188 189Check heap integrity ok! // 堆内存完整性检查 190 191==PID:4== Detected memory leak(s): // 内存泄漏信息及调用栈 192 [Check point]: 193 #00: <check_leak+0x1c4>[0x2da4c] -> /lib/libc.so 194 #01: <main+0x44>[0x878] -> mem_check 195 196 [TID:18 Leak:0x320 byte(s)] Allocated from: 197 #00: <main+0x1c>[0x850] -> mem_check 198 #01: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so 199 200 [TID:18 Leak:0x320 byte(s)] Allocated from: 201 #00: <func+0x14>[0x810] -> mem_check 202 #01: <main+0x3c>[0x870] -> mem_check 203 #02: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so 204 205==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s). 206 207==PID:4== Detected memory leak(s): 208 [Check point]: 209 #00: <check_leak+0x1c4>[0x2da4c] -> /lib/libc.so 210 #01: <exit+0x28>[0x111ec] -> /lib/libc.so 211 212 [TID:18 Leak:0x320 byte(s)] Allocated from: 213 #00: <main+0x1c>[0x850] -> mem_check 214 #01: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so 215 216 [TID:18 Leak:0x320 byte(s)] Allocated from: 217 #00: <func+0x14>[0x810] -> mem_check 218 #01: <main+0x3c>[0x870] -> mem_check 219 #02: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so 220 221==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s). 222 223Check heap integrity ok! 224``` 225 226 227##### 调用栈解析 228 229提供parse_mem_info.sh脚本可以对调用栈进行解析,解析脚本存放的路径:kernel/liteos_a/tools/scripts/parse_memory/parse_mem_info.sh。利用脚本可以将相应的调测信息转换成具体的源码行号,如下命令所示,mem_debug.txt保存的是内存调测信息,elf1、elf2等文件是需要解析的elf文件。 230 231 232``` 233$ ./parse_mem_info.sh mem_debug.txt elf1 elf2 elf3 ... 234``` 235 236例如: 237 238 239``` 240$ ./parse_mem_info.sh mem_debug.txt mem_check 241Compiler is [gcc/llvm]: llvm 242Now using addr2line ... 243 244==PID:4== Heap memory statistics(bytes): 245 [Check point]: 246 #00: <main+0x38>[0x86c] at /usr1/xxx/TEST_ELF/mem_check.c:22 247 #01: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so 248 249 [TID: 18, Used: 0x320] 250 251==PID:4== Total heap: 0x320 byte(s), Peak: 0x320 byte(s) 252 253Check heap integrity ok! 254 255==PID:4== Detected memory leak(s): 256 [Check point]: 257 #00: <check_leak+0x1c4>[0x2da4c] -> /lib/libc.so 258 #01: <main+0x44>[0x878] at /usr1/xxx/TEST_ELF/mem_check.c:28 259 260 [TID:18 Leak:0x320 byte(s)] Allocated from: 261 #00: <main+0x1c>[0x850] at /usr1/xxx/TEST_ELF/mem_check.c:17 262 #01: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so 263 264 [TID:18 Leak:0x320 byte(s)] Allocated from: 265 #00: <func+0x14>[0x810] at /usr1/xxx/TEST_ELF/mem_check.c:9 266 #01: <main+0x3c>[0x870] at /usr1/xxx/TEST_ELF/mem_check.c:24 267 #02: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so 268 269==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s). 270``` 271 272#### 命令行参数方式 273 274 275对用户态进程进行内存相关的检查时,除了接口调用方式还可以通过命令行方式进行内存统计、内存泄漏或内存完整性检查。 276 277``` 278--mwatch:初始化内存调测功能,注册信号。内存调测信息将从串口输出; 279--mrecord <f_path>:初始化内存调测功能,注册信号。内存调测信息将保存至f_path文件,若f_path创建失败,则内存调测信息将从串口输出 280``` 281 282 283在待调测的进程未退出时可使用信号机制获取对应信息: 284 285``` 286kill -35 <pid> # 查看线程级堆内存占用 287kill -36 <pid> # 检查是否存在堆内存泄漏 288kill -37 <pid> # 检查堆内存头节点是否完整 289``` 290 291 292##### 示例代码 293 294代码功能:构造内存问题利用命令行方式进行内存调测。 295 296 297```c 298#include <pthread.h> 299#include <stdlib.h> 300#include <stdio.h> 301 302#define MALLOC_LEAK_SIZE 0x300 303 304void func(void) 305{ 306 char *ptr = malloc(MALLOC_LEAK_SIZE); 307 memset(ptr, '3', MALLOC_LEAK_SIZE); 308} 309 310int main() 311{ 312 char *ptr = malloc(MALLOC_LEAK_SIZE); 313 memset(ptr, '1', MALLOC_LEAK_SIZE); 314 func(); 315 while (1); 316} 317``` 318 319 320##### 编译 321 322参考接口调用章节里的编译。 323 324 325##### 使用mwatch参数命令 326 327 328``` 329OHOS # ./mem_check --mwatch // 利用task命令可以查到mem_check进程的pid为4 330OHOS # 331OHOS # kill -35 4 // 查看堆内存统计信息 332OHOS # 333==PID:4== Heap memory statistics(bytes): 334 [Check point]: 335 #00: <arm_signal_process+0x5c>[0x58dfc] -> /lib/libc.so 336 337 [TID: 18, Used: 0x640] 338 339==PID:4== Total heap: 0x640 byte(s), Peak: 0x640 byte(s) 340 341OHOS # kill -36 4 // 检查是否存在堆内存泄漏 342OHOS # 343==PID:4== Detected memory leak(s): 344 [Check point]: 345 #00: <check_leak+0x1c4>[0x2da4c] -> /lib/libc.so 346 #01: <arm_signal_process+0x5c>[0x58dfc] -> /lib/libc.so 347 348 [TID:18 Leak:0x320 byte(s)] Allocated from: 349 #00: <main+0x14>[0x724] -> mem_check 350 #01: <(null)+0x2555a9dc>[0x219dc] -> /lib/libc.so 351 352 [TID:18 Leak:0x320 byte(s)] Allocated from: 353 #00: <func+0x14>[0x6ec] -> mem_check 354 #01: <main+0x30>[0x740] -> mem_check 355 #02: <(null)+0x2555a9dc>[0x219dc] -> /lib/libc.so 356 357==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s). 358 359OHOS # kill -37 4 // 检查堆内存头节点的完整性 360OHOS # 361Check heap integrity ok! 362``` 363 364 365##### 调用栈解析 366 367将调测信息保存至test.txt文件中,利用脚本进行解析,获取内存泄漏的具体行号。 368 369 370``` 371$ ./parse_mem_info.sh test.txt mem_check 372Compiler is [gcc/llvm]: llvm 373Now using addr2line ... 374 375==PID:4== Detected memory leak(s): 376 [Check point]: 377 #00: <check_leak+0x1c4>[0x2da4c] -> /lib/libc.so 378 #01: <arm_signal_process+0x5c>[0x58dfc] -> /lib/libc.so 379 380 [TID:18 Leak:0x320 byte(s)] Allocated from: 381 #00: <main+0x14>[0x724] at /usr1/xxx/TEST_ELF/mem_check.c:14 382 #01: <(null)+0x2555a9dc>[0x219dc] -> /lib/libc.so 383 384 [TID:18 Leak:0x320 byte(s)] Allocated from: 385 #00: <func+0x14>[0x6ec] at /usr1/xxx/TEST_ELF/mem_check.c:8 386 #01: <main+0x30>[0x740] at /usr1/xxx/TEST_ELF/mem_check.c:19 387 #02: <(null)+0x2555a9dc>[0x219dc] -> /lib/libc.so 388 389==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s). 390``` 391 392 393##### 使用mrecord参数命令 394 3951. 执行用户程序并指定记录内存调测信息的文件路径 396 397 ``` 398 OHOS # ./mem_check --mrecord /storage/check.txt 399 ``` 400 4012. 利用kill -35 <pid>统计内存信息,该信息将会输出到文件中,使用cat命令查看 402 403 ``` 404 OHOS # kill -35 4 405 OHOS # Memory statistics information saved in /storage/pid(4)_check.txt 406 407 OHOS # cat /storage/pid(4)_check.txt 408 409 ==PID:4== Heap memory statistics(bytes): 410 [Check point]: 411 #00: <arm_signal_process+0x5c>[0x5973c] -> /lib/libc.so 412 413 [TID: 18, Used: 0x640] 414 415 ==PID:4== Total heap: 0x640 byte(s), Peak: 0x640 byte(s) 416 ``` 417 4183. 利用kill -36 <pid>校验内存完整性,该信息将会输出到文件中,使用cat命令查看 419 420 ``` 421 OHOS # kill -36 4 422 OHOS # Leak check information saved in /storage/pid(4)_check.txt 423 424 OHOS # cat /storage/pid(4)_check.txt 425 426 ==PID:4== Heap memory statistics(bytes): 427 [Check point]: 428 #00: <arm_signal_process+0x5c>[0x5973c] -> /lib/libc.so 429 430 [TID: 18, Used: 0x640] 431 432 ==PID:4== Total heap: 0x640 byte(s), Peak: 0x640 byte(s) 433 434 ==PID:4== Detected memory leak(s): 435 [Check point]: 436 #00: <check_leak+0x1c4>[0x2e38c] -> /lib/libc.so 437 #01: <arm_signal_process+0x5c>[0x5973c] -> /lib/libc.so 438 439 [TID:18 Leak:0x320 byte(s)] Allocated from: 440 #00: <main+0x14>[0x724] -> mem_check 441 #01: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so 442 443 [TID:18 Leak:0x320 byte(s)] Allocated from: 444 #00: <func+0x14>[0x6ec] -> mem_check 445 #01: <main+0x30>[0x740] -> mem_check 446 #02: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so 447 448 ==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s). 449 ``` 450 4514. 利用kill -9 <pid>杀掉当前进程,进程退出后会默认校验内存完整性,该信息将会输出到文件中,使用cat命令查看 452 453 ``` 454 OHOS # kill -9 4 455 OHOS # Leak check information saved in /storage/pid(4)_check.txt 456 457 Check heap integrity ok! 458 459 OHOS # cat /storage/pid(4)_check.txt 460 OHOS # 461 ==PID:4== Heap memory statistics(bytes): 462 [Check point]: 463 #00: <arm_signal_process+0x5c>[0x5973c] -> /lib/libc.so 464 465 [TID: 18, Used: 0x640] 466 467 ==PID:4== Total heap: 0x640 byte(s), Peak: 0x640 byte(s) 468 469 ==PID:4== Detected memory leak(s): 470 [Check point]: 471 #00: <check_leak+0x1c4>[0x2e38c] -> /lib/libc.so 472 #01: <arm_signal_process+0x5c>[0x5973c] -> /lib/libc.so 473 474 [TID:18 Leak:0x320 byte(s)] Allocated from: 475 #00: <main+0x14>[0x724] -> mem_check 476 #01: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so 477 478 [TID:18 Leak:0x320 byte(s)] Allocated from: 479 #00: <func+0x14>[0x6ec] -> mem_check 480 #01: <main+0x30>[0x740] -> mem_check 481 #02: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so 482 483 ==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s). 484 485 ==PID:4== Detected memory leak(s): 486 [Check point]: 487 #00: <check_leak+0x1c4>[0x2e38c] -> /lib/libc.so 488 #01: <exit+0x28>[0x11b2c] -> /lib/libc.so 489 490 [TID:18 Leak:0x320 byte(s)] Allocated from: 491 #00: <main+0x14>[0x724] -> mem_check 492 #01: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so 493 494 [TID:18 Leak:0x320 byte(s)] Allocated from: 495 #00: <func+0x14>[0x6ec] -> mem_check 496 #01: <main+0x30>[0x740] -> mem_check 497 #02: <(null)+0x1fdd231c>[0x2231c] -> /lib/libc.so 498 499 ==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s). 500 ``` 501 502> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 503> 上述连续记录的信息会逐步追加到初始化时所指定的文件中,故最后cat文件时,文件中还包含历史记录的信息内容。 504## 常见问题 505 506 507### UAF(Use after free) 508 509- 申请小块内存(不大于0x1c000 bytes) 510 free之后: 511 512 读操作:读取free之后的内存大概率是魔术数字(0xFEFEFEFE) 513 514 > ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:** 515 > free之后的堆内存不会立即释放进堆内存池,会先放至固定长度的队列中,并置魔术数字0xFE,队列满后会将先放至队列中的内存块释放进堆内存池 516 517 写操作:无法校验。 518 519 520- 申请大块内存(大于0x1c000 bytes) 521 堆内存由malloc通过调用mmap接口申请,free之后若仍访问该内存,则用户程序异常(该内存区间已被unmap)。 522 523 524### Double free 525 526Double free时,用户程序将会异常退出。 527 528 529### 堆内存节点被踩 530 531- 申请小块内存(不大于0x1c000 bytes) 532 533 堆内存节点被踩时,用户程序将会异常退出,并输出破坏被踩节点的可能的堆内存申请调用栈,对于野指针踩内存情况无法校验出来。例如用户程序mem_check中存在堆内存越界踩的情况,利用命令行方式可以获得踩内存的可能的具体位置。 534 535 536 ``` 537 OHOS # ./mem_check --mwatch 538 OHOS # 539 ==PID:6== Memory integrity information: 540 [TID:28 allocated addr: 0x272e1ea0, size: 0x120] The possible attacker was allocated from: 541 #00: <malloc+0x808>[0x640e8] -> /lib/libc.so 542 #01: <threadFunc1+0x7c>[0x21d0] -> mem_check 543 ``` 544 545 可以通过调用栈解析脚本对调用栈信息进行解析。 546 547- 申请大块内存(大于0x1c000 bytes) 548 549 堆内存由malloc通过mmap接口申请,申请得到的堆内存块前后各置一个size为PAGE_SIZE大小的区间,设置无读写权限,读写操作会触发用户程序异常。 550 551