1# LMS调测 2 3- [基本概念](#基本概念) 4- [运行机制](#运行机制) 5- [接口说明](#接口说明) 6 - [内核态](#内核态) 7 - [用户态](#用户态) 8- [开发指导](#开发指导) 9 - [内核态开发流程](#内核态开发流程) 10- [内核态编程实例](#内核态编程实例) 11- [内核态示例代码](#内核态示例代码) 12 - [内核态结果验证](#内核态结果验证) 13 - [用户态开发流程](#用户态开发流程) 14 - [用户态编程实例](#用户态编程实例) 15 - [用户态示例代码](#用户态示例代码) 16 - [用户态结果验证](#用户态结果验证) 17 18## 基本概念 19 20LMS全称为Lite Memory Sanitizer,是一种实时检测内存操作合法性的调测工具。LMS能够实时检测缓冲区溢出(buffer overflow),释放后使用(use after free) 和重复释放(double Free), 在异常发生的第一时间通知操作系统,结合backtrace等定位手段,能准确定位到产生内存问题的代码行,极大提升内存问题定位效率。 21 22OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能: 23 24- 支持多内存池检测; 25 26- 支持LOS_MemAlloc、LOS_MemAllocAlign、LOS_MemRealloc申请出的内存检测; 27 28- 支持安全函数的访问检测(默认开启); 29 30- 支持libc 高频函数的访问检测,包括:memset、memcpy、memmove、strcat、strcpy、strncat、strncpy。 31 32 33## 运行机制 34 35LMS使用影子内存映射标记系统内存的状态,一共可标记为三个状态:可读写,不可读写,已释放。影子内存存放在内存池的尾部。 36 37- 内存从堆上申请后,会将数据区的影子内存设置为“可读写”状态,并将头结点区的影子内存设置为“不可读写”状态。 38 39- 内存在堆上被释放时,会将被释放内存的影子内存设置为“已释放”状态。 40 41- 编译代码时,会在代码中的读写指令前插入检测函数,对地址的合法性进行检验。主要是检测访问内存的影子内存的状态值,若检测到影子内存为不可读写,则会报溢出错误;若检测到影子内存为已释放,则会报释放后使用错误。 42 43- 在内存释放时,会检测被释放地址的影子内存状态值,若检测到影子内存非可读写,则会报重复释放错误。 44 45 46## 接口说明 47 48 49### 内核态 50 51OpenHarmony LiteOS-A内核的LMS模块提供下面几种功能,接口详细信息可以查看[API](https://gitee.com/openharmony/kernel_liteos_a/blob/master/kernel/include/los_lms.h)参考。 52 53**表1** LMS模块接口说明 54 55| 功能分类 | 接口名 | 描述 | 56| -------- | -------- | -------- | 57| 添加指定内存池被检测 | LOS_LmsCheckPoolAdd | 将指定内存池的地址范围添加到LMS的内存检测链表上,当访问的地址在链表范围内时,LMS才进行合法性校验;且LOS_MemInit接口会调用该接口,默认将初始化的内存池挂入到检测链表中。 | 58| 删除指定内存池不被检测 | LOS_LmsCheckPoolDel | 不检测指定内存池地址范围内的合法性校验。 | 59| 使能指定内存段锁保护 | LOS_LmsAddrProtect | 为某段内存地址上锁,设置为不可读写,一旦访问则报错。 | 60| 去能指定内存段锁保护 | LOS_LmsAddrDisableProtect | 为某段内存地址解锁,设置为可读写。 | 61 62 63### 用户态 64 65用户态仅提供LMS检测库,不提供对外接口。 66 67 68## 开发指导 69 70 71### 内核态开发流程 72 73开启LMS调测的典型流程如下: 74 751. 配置LMS模块相关宏。 76 配置LMS控制宏LOSCFG_KERNEL_LMS,默认关,在kernel/liteos_a目录下执行 make update_config命令配置"Kernel->Enable Lite Memory Sanitizer"中打开YES: 77 78 | 宏 | menuconfig选项 | 含义 | 取值 | 79 | -------- | -------- | -------- | -------- | 80 | LOSCFG_KERNEL_LMS | Enable Lms Feature | Lms模块的裁剪开关 | YES/NO | 81 | LOSCFG_LMS_MAX_RECORD_POOL_NUM | Lms check pool max num | LMS支持的检测内存池最大个数 | INT | 82 | LOSCFG_LMS_LOAD_CHECK | Enable lms read check | LMS内存读检测的裁剪开关 | YES/NO | 83 | LOSCFG_LMS_STORE_CHECK | Enable lms write check | LMS内存写检测的裁剪开关 | YES/NO | 84 | LOSCFG_LMS_CHECK_STRICT | Enable lms strict check, byte-by-byte | LMS内存逐字节严格检测的裁剪开关 | YES/NO | 85 862. 在被检测模块的编译脚本中,修改编译选项。 87 增加LMS检测编译选项-fsanitize=kernel-address。为避免编译器优化,增加-O0编译选项。 88 89 gcc与clang编译选项存在差异,参照如下示例: 90 ``` 91 if ("$ohos_build_compiler_specified" == "gcc") { 92 cflags_c = [ 93 "-O0", 94 "-fsanitize=kernel-address", 95 ] 96 } else { 97 cflags_c = [ 98 "-O0", 99 "-fsanitize=kernel-address", 100 "-mllvm", 101 "-asan-instrumentation-with-call-threshold=0", 102 "-mllvm", 103 "-asan-stack=0", 104 "-mllvm", 105 "-asan-globals=0", 106 ] 107 } 108 ``` 109 1103. 重新编译,查看串口输出。如果检测到内存问题,会输出检测结果。 111 112 113## 内核态编程实例 114 115本实例实现如下功能: 116 1171. 创建一个用于Lms测试的任务。 118 1192. 构造内存溢出错误和释放后使用错误。 120 1213. 添加-fsanitize=kernel-address后编译执行,观察输出结果 122 123 124## 内核态示例代码 125 126实例代码如下: 127``` 128#define PAGE_SIZE (0x1000U) 129#define INDEX_MAX 20 130UINT32 g_lmsTestTaskId; 131char g_testLmsPool[2 * PAGE_SIZE]; 132STATIC VOID testPoolInit(void) 133{ 134 UINT32 ret = LOS_MemInit(g_testLmsPool, 2 * PAGE_SIZE); 135 if (ret != 0) { 136 PRINT_ERR("%s failed, ret = 0x%x\n", __FUNCTION__, ret); 137 return; 138 } 139} 140static VOID LmsTestOsmallocOverflow(VOID) 141{ 142 PRINTK("\n######%s start ######\n", __FUNCTION__); 143 UINT32 i; 144 CHAR *str = (CHAR *)LOS_MemAlloc(g_testLmsPool, INDEX_MAX); 145 PRINTK("str[%2d]=0x%2x ", INDEX_MAX, str[INDEX_MAX]); /* trigger heap overflow at str[INDEX_MAX] */ 146 PRINTK("\n######%s stop ######\n", __FUNCTION__); 147} 148static VOID LmsTestUseAfterFree(VOID) 149{ 150 PRINTK("\n######%s start ######\n", __FUNCTION__); 151 UINT32 i; 152 CHAR *str = (CHAR *)LOS_MemAlloc(g_testLmsPool, INDEX_MAX); 153 LOS_MemFree(g_testLmsPool, str); 154 PRINTK("str[%2d]=0x%2x ", 0, str[0]); /* trigger use after free at str[0] */ 155 PRINTK("\n######%s stop ######\n", __FUNCTION__); 156} 157VOID LmsTestCaseTask(VOID) 158{ 159 testPoolInit(); 160 LmsTestOsmallocOverflow(); 161 LmsTestUseAfterFree(); 162} 163UINT32 Example_Lms_test(VOID){ 164 UINT32 ret; 165 TSK_INIT_PARAM_S lmsTestTask; 166 /* 创建用于lms测试的任务 */ 167 memset(&lmsTestTask, 0, sizeof(TSK_INIT_PARAM_S)); 168 lmsTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)LmsTestCaseTask; 169 lmsTestTask.pcName = "TestLmsTsk"; /* 测试任务名称 */ 170 lmsTestTask.uwStackSize = 0x800; 171 lmsTestTask.usTaskPrio = 5; 172 lmsTestTask.uwResved = LOS_TASK_STATUS_DETACHED; 173 ret = LOS_TaskCreate(&g_lmsTestTaskId, &lmsTestTask); 174 if(ret != LOS_OK){ 175 PRINT_ERR("LmsTestTask create failed .\n"); 176 return LOS_NOK; 177 } 178 return LOS_OK; 179} 180LOS_MODULE_INIT(Example_Lms_test, LOS_INIT_LEVEL_KMOD_EXTENDED); 181``` 182 183 184### 内核态结果验证 185 186输出结果如下: 187``` 188######LmsTestOsmallocOverflow start ###### 189[ERR][KProcess:LmsTestCaseTask]* Kernel Address Sanitizer Error Detected Start * 190[ERR][KProcess:LmsTestCaseTask]Heap buffer overflow error detected 191[ERR][KProcess:LmsTestCaseTask]Illegal READ address at: [0x4157a3c8] 192[ERR][KProcess:LmsTestCaseTask]Shadow memory address: [0x4157be3c : 4] Shadow memory value: [2] 193OsBackTrace fp = 0x402c0f88 194runTask->taskName = LmsTestCaseTask 195runTask->taskID = 2 196***backtrace begin*** 197traceback fp fixed, trace using fp = 0x402c0fd0 198traceback 0 -- lr = 0x400655a4 fp = 0x402c0ff8 199traceback 1 -- lr = 0x40065754 fp = 0x402c1010 200traceback 2 -- lr = 0x40044bd0 fp = 0x402c1038 201traceback 3 -- lr = 0x40004e14 fp = 0xcacacaca 202[LMS] Dump info around address [0x4157a3c8]: 203 [0x4157a3a0]: 00 00 00 00 00 00 00 00 | [0x4157be3a | 0]: 1 1 204 [0x4157a3a8]: ba dc cd ab 00 00 00 00 | [0x4157be3a | 4]: 2 2 205 [0x4157a3b0]: 20 00 00 80 00 00 00 00 | [0x4157be3b | 0]: 2 0 206 [0x4157a3b8]: 00 00 00 00 00 00 00 00 | [0x4157be3b | 4]: 0 0 207 [0x4157a3c0]: 00 00 00 00 00 00 00 00 | [0x4157be3c | 0]: 0 0 208 [0x4157a3c8]: [ba] dc cd ab a8 a3 57 41 | [0x4157be3c | 4]: [2] 2 209 [0x4157a3d0]: 2c 1a 00 00 00 00 00 00 | [0x4157be3d | 0]: 2 3 210 [0x4157a3d8]: 00 00 00 00 00 00 00 00 | [0x4157be3d | 4]: 3 3 211 [0x4157a3e0]: 00 00 00 00 00 00 00 00 | [0x4157be3e | 0]: 3 3 212 [0x4157a3e8]: 00 00 00 00 00 00 00 00 | [0x4157be3e | 4]: 3 3 213 [0x4157a3f0]: 00 00 00 00 00 00 00 00 | [0x4157be3f | 0]: 3 3 214[ERR][KProcess:LmsTestCaseTask]* Kernel Address Sanitizer Error Detected End * 215str[20]=0xffffffba 216######LmsTestOsmallocOverflow stop ###### 217###### LmsTestUseAfterFree start ###### 218[ERR][KProcess:LmsTestCaseTask]* Kernel Address Sanitizer Error Detected Start * 219[ERR][KProcess:LmsTestCaseTask]Use after free error detected 220[ERR][KProcess:LmsTestCaseTask]Illegal READ address at: [0x4157a3d4] 221[ERR][KProcess:LmsTestCaseTask]Shadow memory address: [0x4157be3d : 2] Shadow memory value: [3] 222OsBackTrace fp = 0x402c0f90 223runTask->taskName = LmsTestCaseTask 224runTask->taskID = 2 225***backtrace begin*** 226traceback fp fixed, trace using fp = 0x402c0fd8 227traceback 0 -- lr = 0x40065680 fp = 0x402c0ff8 228traceback 1 -- lr = 0x40065758 fp = 0x402c1010 229traceback 2 -- lr = 0x40044bd0 fp = 0x402c1038 230traceback 3 -- lr = 0x40004e14 fp = 0xcacacaca 231[LMS] Dump info around address [0x4157a3d4]: 232 [0x4157a3a8]: ba dc cd ab 00 00 00 00 | [0x4157be3a | 4]: 2 2 233 [0x4157a3b0]: 20 00 00 80 00 00 00 00 | [0x4157be3b | 0]: 2 0 234 [0x4157a3b8]: 00 00 00 00 00 00 00 00 | [0x4157be3b | 4]: 0 0 235 [0x4157a3c0]: 00 00 00 00 00 00 00 00 | [0x4157be3c | 0]: 0 0 236 [0x4157a3c8]: ba dc cd ab a8 a3 57 41 | [0x4157be3c | 4]: 2 2 237 [0x4157a3d0]: 2c 1a 00 00 [00] 00 00 00 | [0x4157be3d | 0]: 2 [3] 238 [0x4157a3d8]: 00 00 00 00 00 00 00 00 | [0x4157be3d | 4]: 3 3 239 [0x4157a3e0]: 00 00 00 00 00 00 00 00 | [0x4157be3e | 0]: 3 3 240 [0x4157a3e8]: ba dc cd ab c8 a3 57 41 | [0x4157be3e | 4]: 2 2 241 [0x4157a3f0]: 0c 1a 00 00 00 00 00 00 | [0x4157be3f | 0]: 2 3 242 [0x4157a3f8]: 00 00 00 00 00 00 00 00 | [0x4157be3f | 4]: 3 3 243[ERR][KProcess:LmsTestCaseTask]* Kernel Address Sanitizer Error Detected End * 244str[ 0]=0x 0 245######LmsTestUseAfterFree stop ###### 246``` 247 248输出的关键信息包括: 249 250- 错误类型: 251 - Heap buffer overflow堆内存越界 252 - Use after free 释放后使用 253 254- 错误操作: 255 - Illegal Read非法读 256 - Illegal Write非法写 257 - Illegal Double free重复释放 258 259- 上下文: 260 - 当前任务信息(taskName, taskId) 261 - 回溯栈(backtrace) 262 263- 出错地址的内存信息: 264 - 内存的值、及对应影子内存的值 265 - 内存地址:内存值| [影子内存地址 | 影子内存字节内偏移]:影子内存值 266 - 影子内存值:0(可读可写)、3(已释放)、2(红区)、1(填充值)。 267 268 269### 用户态开发流程 270 271在待检测的app编译脚本中,添加如下参数即可, 完整示例可参见/kernel/liteos_a/apps/lms/BUILD.gn。 272 273``` 274if ("$ohos_build_compiler_specified" == "gcc") { 275 cflags_c = [ 276 "-O0", 277 "-fsanitize=kernel-address", 278 "-funwind-tables", 279 "-fasynchronous-unwind-tables", 280 ] 281 } else { 282 cflags_c = [ 283 "-O0", 284 "-fsanitize=kernel-address", 285 "-mllvm", 286 "-asan-instrumentation-with-call-threshold=0", 287 "-mllvm", 288 "-asan-stack=0", 289 "-mllvm", 290 "-asan-globals=0", 291 "-funwind-tables", 292 "-fasynchronous-unwind-tables", 293 ] 294 } 295 ldflags = [ 296 "-rdynamic", 297 "-lunwind", 298 "-lusrlms", 299 "-Wl,--wrap=realloc", 300 "-Wl,--wrap=calloc", 301 "-Wl,--wrap=malloc", 302 "-Wl,--wrap=free", 303 "-Wl,--wrap=valloc", 304 "-Wl,--wrap=aligned_alloc", 305 "-Wl,--wrap=memset", 306 "-Wl,--wrap=memcpy", 307 "-Wl,--wrap=memmove", 308 "-Wl,--wrap=strcpy", 309 "-Wl,--wrap=strcat", 310 ] 311 deps = [ "//kernel/liteos_a/kernel/extended/lms/usr:usrlmslib" ] 312``` 313 314 315### 用户态编程实例 316 317本实例实现如下功能: 318 3191. 构造内存溢出错误和释放后使用错误。 320 3212. 添加对应编译选项后,重新编译执行 322 323 324### 用户态示例代码 325 326实例代码如下: 327``` 328static void BufWriteTest(void *buf, int start, int end) 329{ 330 for (int i = start; i <= end; i++) { 331 ((char *)buf)[i] = 'a'; 332 } 333} 334static void BufReadTest(void *buf, int start, int end) 335{ 336 char tmp; 337 for (int i = start; i <= end; i++) { 338 tmp = ((char *)buf)[i]; 339 } 340} 341static void LmsMallocTest(void) 342{ 343 printf("\n-------- LmsMallocTest Start --------\n"); 344 char *buf = (char *)malloc(16); 345 BufReadTest(buf, -1, 16); 346 free(buf); 347 printf("\n-------- LmsMallocTest End --------\n"); 348} 349static void LmsFreeTest(void) 350{ 351 printf("\n-------- LmsFreeTest Start --------\n"); 352 char *buf = (char *)malloc(16); 353 free(buf); 354 BufReadTest(buf, 1, 1); 355 free(buf); 356 printf("\n-------- LmsFreeTest End --------\n"); 357} 358int main(int argc, char * const * argv) 359{ 360 printf("\n############### Lms Test start ###############\n"); 361 char *tmp = (char *)malloc(5000); 362 LmsMallocTest(); 363 LmsFreeTest(); 364 printf("\n############### Lms Test End ###############\n"); 365} 366``` 367 368 369### 用户态结果验证 370 371输出结果如下: 372``` 373* Lite Memory Sanitizer Error Detected * 374Heap buffer overflow error detected! 375Illegal READ address at: [0x1f8b3edf] 376Shadow memory address: [0x3d34d3ed : 6] Shadow memory value: [2] 377Accessable heap addr 0 378Heap red zone 2 379Heap freed buffer 3 380Dump info around address [0x1f8b3edf]: 381 [0x1f8b3eb8]: 74 55 8b 1f 74 55 8b 1f | [0x3d34d3eb | 4]: 0 0 382 [0x1f8b3ec0]: f8 9c 8b 1f 00 00 00 00 | [0x3d34d3ec | 0]: 0 0 383 [0x1f8b3ec8]: 00 00 00 00 9c fc fc fc | [0x3d34d3ec | 4]: 0 0 384 [0x1f8b3ed0]: 21 00 00 00 41 00 00 00 | [0x3d34d3ed | 0]: 0 0 385 [0x1f8b3ed8]: 60 55 8b 1f 60 55 8b [1f]| [0x3d34d3ed | 4]: 2 [2] 386 [0x1f8b3ee0]: 50 4e 0b 00 00 00 00 00 | [0x3d34d3ee | 0]: 0 0 387 [0x1f8b3ee8]: 09 00 00 00 00 00 00 00 | [0x3d34d3ee | 4]: 0 0 388 [0x1f8b3ef0]: 00 00 00 00 08 03 09 00 | [0x3d34d3ef | 0]: 2 2 389 [0x1f8b3ef8]: 00 00 00 00 00 00 00 00 | [0x3d34d3ef | 4]: 2 2 390* Lite Memory Sanitizer Error Detected End * 391Backtrace() returned 5 addresses 392 #01: <LMS_ReportError+0x284>[0x4d6c] -> ./sample_usr_lms 393 #02: <(null)+0x2004074>[0x4074] -> ./sample_usr_lms 394 #03: <(null)+0x2003714>[0x3714] -> ./sample_usr_lms 395 #04: <main+0x40>[0x363c] -> ./sample_usr_lms 396 #05: <(null)+0x1f856f30>[0x56f30] -> /lib/libc.so 397-------- LMS_malloc_test End -------- 398* Lite Memory Sanitizer Error Detected * 399Use after free error detected! 400Illegal Double free address at: [0x1f8b3ee0] 401Shadow memory address: [0x3d34d3ee : 0] Shadow memory value: [3] 402Accessable heap addr 0 403Heap red zone 2 404Heap freed buffer 3 405Dump info around address [0x1f8b3ee0]: 406 [0x1f8b3ec0]: f8 9c 8b 1f 00 00 00 00 | [0x3d34d3ec | 0]: 0 0 407 [0x1f8b3ec8]: 00 00 00 00 fc fd fc fc | [0x3d34d3ec | 4]: 0 0 408 [0x1f8b3ed0]: 21 00 00 00 20 01 00 00 | [0x3d34d3ed | 0]: 0 0 409 [0x1f8b3ed8]: 60 55 8b 1f 60 55 8b 1f | [0x3d34d3ed | 4]: 2 2 410 [0x1f8b3ee0]: [20] 60 9a 1f 40 61 9a 1f | [0x3d34d3ee | 0]: [3] 3 411 [0x1f8b3ee8]: 60 62 9a 1f 80 63 9a 1f | [0x3d34d3ee | 4]: 3 3 412 [0x1f8b3ef0]: 20 40 8b 1f 20 20 8b 1f | [0x3d34d3ef | 0]: 3 3 413 [0x1f8b3ef8]: 00 00 00 00 00 00 00 00 | [0x3d34d3ef | 4]: 3 3 414 [0x1f8b3f00]: 00 00 00 00 00 00 00 00 | [0x3d34d3f0 | 0]: 3 3 415* Lite Memory Sanitizer Error Detected End * 416Backtrace() returned 5 addresses 417 #01: <LMS_ReportError+0x284>[0x4d6c] -> ./sample_usr_lms 418 #02: <LMS_free+0xcc>[0x5548] -> ./sample_usr_lms 419 #03: <(null)+0x2003fc4>[0x3fc4] -> ./sample_usr_lms 420 #04: <main+0x68>[0x3664] -> ./sample_usr_lms 421 #05: <(null)+0x1f856f30>[0x56f30] -> /lib/libc.so 422-------- LMS_free_test End -------- 423``` 424 425输出的Backtrace中包含地址所在的文件名,用户需查找对应文件中地址对应的代码行号。 426