1# LMS调测 2 3 4## 基本概念 5 6LMS(Lite Memory Sanitizer)是一种实时检测内存操作合法性的调测工具。LMS能够实时检测缓冲区溢出(buffer overflow),释放后使用(use after free) 和重复释放(double free), 在异常发生的第一时间通知操作系统,结合backtrace等定位手段,能准确定位到产生内存问题的代码行,极大提升内存问题定位效率。 7 8OpenHarmony LiteOS-M内核的LMS模块提供下面几种功能: 9 10- 支持多内存池检测。 11 12- 支持LOS_MemAlloc、LOS_MemAllocAlign、LOS_MemRealloc申请出的内存检测。 13 14- 支持安全函数的访问检测(默认开启)。 15 16- 支持libc 高频函数的访问检测,包括:memset、memcpy、memmove、strcat、strcpy、strncat、strncpy。 17 18 19## 运行机制 20 21LMS使用影子内存映射标记系统内存的状态,一共可标记为三个状态:可读写,不可读写,已释放。影子内存存放在内存池的尾部。 22 23- 内存从堆上申请后,会将数据区的影子内存设置为“可读写”状态,并将头结点区的影子内存设置为“不可读写”状态。 24 25- 内存在堆上被释放时,会将被释放内存的影子内存设置为“已释放”状态。 26 27- 编译代码时,会在代码中的读写指令前插入检测函数,对地址的合法性进行检验。主要是检测访问内存的影子内存的状态值,若检测到影子内存为不可读写,则会报溢出错误;若检测到影子内存为已释放,则会报释放后使用错误。 28 29- 在内存释放时,会检测被释放地址的影子内存状态值,若检测到影子内存非可读写,则会报重复释放错误。 30 31 32## 接口说明 33 34OpenHarmony LiteOS-M内核的LMS模块提供下面几种功能,接口详细信息可以查看[API](https://gitee.com/openharmony/kernel_liteos_m/blob/master/components/lms/los_lms.h)参考。 35 36 **表1** LMS模块接口说明 37 38| 功能分类 | 接口名 | 描述 | 39| -------- | -------- | -------- | 40| 添加指定内存池被检测 | LOS_LmsCheckPoolAdd | 将指定内存池的地址范围添加到LMS的内存检测链表上,当访问的地址在链表范围内时,LMS才进行合法性校验;且LOS_MemInit接口会调用该接口,默认将初始化的内存池挂入到检测链表中。 | 41| 删除指定内存池不被检测 | LOS_LmsCheckPoolDel | 不检测指定内存池地址范围内的合法性校验。 | 42| 使能指定内存段锁保护 | LOS_LmsAddrProtect | 为某段内存地址上锁,设置为不可读写,一旦访问则报错。 | 43| 去能指定内存段锁保护 | LOS_LmsAddrDisableProtect | 为某段内存地址解锁,设置为可读写。 | 44 45 46## 开发指导 47 48 49### 开发流程 50 51开启LMS调测的典型流程如下: 52 531. 配置LMS模块相关宏。 54 配置LMS控制宏LOSCFG_KERNEL_LMS,默认关,在kernel/liteos_m目录下执行 make menuconfig命令配置"Kernel->Enable Lite Memory Sanitizer"中打开YES(如果没有这个选项,需要先勾选Enable Backtrace): 55 56 | 宏 | menuconfig选项 | 含义 | 取值 | 57 | -------- | -------- | -------- | -------- | 58 | LOSCFG_KERNEL_LMS | Enable Lms Feature | Lms模块的裁剪开关 | YES/NO | 59 | LOSCFG_LMS_MAX_RECORD_POOL_NUM | Lms check pool max num | LMS支持的检测内存池最大个数 | INT | 60 | LOSCFG_LMS_LOAD_CHECK | Enable lms read check | LMS内存读检测的裁剪开关 | YES/NO | 61 | LOSCFG_LMS_STORE_CHECK | Enable lms write check | LMS内存写检测的裁剪开关 | YES/NO | 62 | LOSCFG_LMS_CHECK_STRICT | Enable lms strict check, byte-by-byte | LMS内存逐字节严格检测的裁剪开关 | YES/NO | 63 642. 在被检测模块的编译脚本中,修改编译选项。 65 增加LMS检测编译选项-fsanitize=kernel-address。为避免编译器优化,增加-O0编译选项。 66 67 gcc与clang编译选项存在差异,参照如下示例: 68 69 ``` 70 if ("$ohos_build_compiler_specified" == "gcc") { 71 cflags_c = [ 72 "-O0", 73 "-fsanitize=kernel-address", 74 ] 75 } else { 76 cflags_c = [ 77 "-O0", 78 "-fsanitize=kernel-address", 79 "-mllvm", 80 "-asan-instrumentation-with-call-threshold=0", 81 "-mllvm", 82 "-asan-stack=0", 83 "-mllvm", 84 "-asan-globals=0", 85 ] 86 } 87 ``` 88 893. 重新编译,查看串口输出。如果检测到内存问题,会输出检测结果。 90 91 92### 编程实例 93 94本实例实现如下功能: 95 961. 创建一个用于Lms测试的任务。 97 982. 构造内存溢出错误和释放后使用错误。 99 1003. 添加-fsanitize=kernel-address后编译执行,观察输出结果 101 102 103### 示例代码 104 105 实例代码如下: 106 107 本演示代码在 ./kernel/liteos_m/testsuites/src/osTest.c 中编译验证,在TestTaskEntry中调用验证入口函数Example_Lms_test。 108 109 请按上文避免编译器优化一节内容,修改osTest.c对应的 ./kernel/liteos_m/testsuites/BUILD.gn。 110 111``` 112#define PAGE_SIZE (0x1000U) 113#define INDEX_MAX 20 114UINT32 g_lmsTestTaskId; 115char g_testLmsPool[2 * PAGE_SIZE]; 116STATIC VOID testPoolInit(void) 117{ 118 UINT32 ret = LOS_MemInit(g_testLmsPool, 2 * PAGE_SIZE); 119 if (ret != 0) { 120 PRINT_ERR("%s failed, ret = 0x%x\n", __FUNCTION__, ret); 121 return; 122 } 123} 124static VOID LmsTestOsmallocOverflow(VOID) 125{ 126 PRINTK("\n######%s start ######\n", __FUNCTION__); 127 UINT32 i; 128 CHAR *str = (CHAR *)LOS_MemAlloc(g_testLmsPool, INDEX_MAX); 129 PRINTK("str[%2d]=0x%2x ", INDEX_MAX, str[INDEX_MAX]); /* trigger heap overflow at str[INDEX_MAX] */ 130 PRINTK("\n######%s stop ######\n", __FUNCTION__); 131} 132static VOID LmsTestUseAfterFree(VOID) 133{ 134 PRINTK("\n######%s start ######\n", __FUNCTION__); 135 UINT32 i; 136 CHAR *str = (CHAR *)LOS_MemAlloc(g_testLmsPool, INDEX_MAX); 137 LOS_MemFree(g_testLmsPool, str); 138 PRINTK("str[%2d]=0x%2x ", 0, str[0]); /* trigger use after free at str[0] */ 139 PRINTK("\n######%s stop ######\n", __FUNCTION__); 140} 141VOID LmsTestCaseTask(VOID) 142{ 143 testPoolInit(); 144 LmsTestOsmallocOverflow(); 145 LmsTestUseAfterFree(); 146} 147UINT32 Example_Lms_test(VOID){ 148 UINT32 ret; 149 TSK_INIT_PARAM_S lmsTestTask; 150 /* 创建用于lms测试的任务 */ 151 memset(&lmsTestTask, 0, sizeof(TSK_INIT_PARAM_S)); 152 lmsTestTask.pfnTaskEntry = (TSK_ENTRY_FUNC)LmsTestCaseTask; 153 lmsTestTask.pcName = "TestLmsTsk"; /* 测试任务名称 */ 154 lmsTestTask.uwStackSize = 0x800; 155 lmsTestTask.usTaskPrio = 5; 156 lmsTestTask.uwResved = LOS_TASK_STATUS_DETACHED; 157 ret = LOS_TaskCreate(&g_lmsTestTaskId, &lmsTestTask); 158 if(ret != LOS_OK){ 159 PRINT_ERR("LmsTestTask create failed .\n"); 160 return LOS_NOK; 161 } 162 return LOS_OK; 163} 164``` 165 166 167### 结果验证 168 169 输出结果示例如下 (根据实际运行环境,数据会有差异): 170 171``` 172######LmsTestOsmallocOverflow start ###### 173[ERR][TestLmsTsk]***** Kernel Address Sanitizer Error Detected Start ***** 174[ERR][TestLmsTsk]Heap buffer overflow error detected 175[ERR][TestLmsTsk]Illegal READ address at: [0x21040414] 176[ERR][TestLmsTsk]Shadow memory address: [0x21041e84 : 6] Shadow memory value: [2] 177psp, start = 21057d88, end = 21057e80 178taskName = TestLmsTsk 179taskID = 5 180----- traceback start ----- 181traceback 0 -- lr = 0x210099f4 182traceback 1 -- lr = 0x2101da6e 183traceback 2 -- lr = 0x2101db38 184traceback 3 -- lr = 0x2101c494 185----- traceback end ----- 186 187[LMS] Dump info around address [0x21040414]: 188 189 [0x21040390]: 00 00 00 00 00 00 00 00 | [0x21041e7c | 4]: 1 1 190 [0x21040398]: 00 00 00 00 00 00 00 00 | [0x21041e7d | 0]: 1 1 191 [0x210403a0]: 00 00 00 00 00 00 00 00 | [0x21041e7d | 4]: 1 1 192 [0x210403a8]: 00 00 00 00 00 00 00 00 | [0x21041e7e | 0]: 1 1 193 [0x210403b0]: 00 00 00 00 00 00 00 00 | [0x21041e7e | 4]: 1 1 194 [0x210403b8]: 00 00 00 00 00 00 00 00 | [0x21041e7f | 0]: 1 1 195 [0x210403c0]: 00 00 00 00 00 00 00 00 | [0x21041e7f | 4]: 1 1 196 [0x210403c8]: 00 00 00 00 00 00 00 00 | [0x21041e80 | 0]: 1 1 197 [0x210403d0]: 00 00 00 00 00 00 00 00 | [0x21041e80 | 4]: 1 1 198 [0x210403d8]: 00 00 00 00 00 00 00 00 | [0x21041e81 | 0]: 1 1 199 [0x210403e0]: 00 00 00 00 00 00 00 00 | [0x21041e81 | 4]: 1 1 200 [0x210403e8]: 00 00 00 00 00 00 00 00 | [0x21041e82 | 0]: 1 1 201 [0x210403f0]: 00 00 00 00 00 00 00 00 | [0x21041e82 | 4]: 1 1 202 [0x210403f8]: 40 1e 04 21 05 07 00 80 | [0x21041e83 | 0]: 2 2 203 [0x21040400]: 00 00 00 00 00 00 00 00 | [0x21041e83 | 4]: 0 0 204 [0x21040408]: 00 00 00 00 00 00 00 00 | [0x21041e84 | 0]: 0 0 205 [0x21040410]: 00 00 00 00 [f8] 03 04 21 | [0x21041e84 | 4]: 0 [2] 206 [0x21040418]: 00 8b 06 00 00 00 00 00 | [0x21041e85 | 0]: 2 3 207 [0x21040420]: 00 00 00 00 00 00 00 00 | [0x21041e85 | 4]: 3 3 208 [0x21040428]: 00 00 00 00 00 00 00 00 | [0x21041e86 | 0]: 3 3 209 [0x21040430]: 00 00 00 00 00 00 00 00 | [0x21041e86 | 4]: 3 3 210 [0x21040438]: 00 00 00 00 00 00 00 00 | [0x21041e87 | 0]: 3 3 211 [0x21040440]: 00 00 00 00 00 00 00 00 | [0x21041e87 | 4]: 3 3 212 [0x21040448]: 00 00 00 00 00 00 00 00 | [0x21041e88 | 0]: 3 3 213 [0x21040450]: 00 00 00 00 00 00 00 00 | [0x21041e88 | 4]: 3 3 214 [0x21040458]: 00 00 00 00 00 00 00 00 | [0x21041e89 | 0]: 3 3 215 [0x21040460]: 00 00 00 00 00 00 00 00 | [0x21041e89 | 4]: 3 3 216 [0x21040468]: 00 00 00 00 00 00 00 00 | [0x21041e8a | 0]: 3 3 217 [0x21040470]: 00 00 00 00 00 00 00 00 | [0x21041e8a | 4]: 3 3 218 [0x21040478]: 00 00 00 00 00 00 00 00 | [0x21041e8b | 0]: 3 3 219 [0x21040480]: 00 00 00 00 00 00 00 00 | [0x21041e8b | 4]: 3 3 220 [0x21040488]: 00 00 00 00 00 00 00 00 | [0x21041e8c | 0]: 3 3 221 [0x21040490]: 00 00 00 00 00 00 00 00 | [0x21041e8c | 4]: 3 3 222[ERR][TestLmsTsk]***** Kernel Address Sanitizer Error Detected End ***** 223str[20]=0xfffffff8 224######LmsTestOsmallocOverflow stop ###### 225 226######LmsTestUseAfterFree start ###### 227[ERR][TestLmsTsk]***** Kernel Address Sanitizer Error Detected Start ***** 228[ERR][TestLmsTsk]Use after free error detected 229[ERR][TestLmsTsk]Illegal READ address at: [0x2104041c] 230[ERR][TestLmsTsk]Shadow memory address: [0x21041e85 : 2] Shadow memory value: [3] 231psp, start = 21057d90, end = 21057e80 232taskName = TestLmsTsk 233taskID = 5 234----- traceback start ----- 235traceback 0 -- lr = 0x210099f4 236traceback 1 -- lr = 0x2101daec 237traceback 2 -- lr = 0x2101db3c 238traceback 3 -- lr = 0x2101c494 239----- traceback end ----- 240 241[LMS] Dump info around address [0x2104041c]: 242 243 [0x21040398]: 00 00 00 00 00 00 00 00 | [0x21041e7d | 0]: 1 1 244 [0x210403a0]: 00 00 00 00 00 00 00 00 | [0x21041e7d | 4]: 1 1 245 [0x210403a8]: 00 00 00 00 00 00 00 00 | [0x21041e7e | 0]: 1 1 246 [0x210403b0]: 00 00 00 00 00 00 00 00 | [0x21041e7e | 4]: 1 1 247 [0x210403b8]: 00 00 00 00 00 00 00 00 | [0x21041e7f | 0]: 1 1 248 [0x210403c0]: 00 00 00 00 00 00 00 00 | [0x21041e7f | 4]: 1 1 249 [0x210403c8]: 00 00 00 00 00 00 00 00 | [0x21041e80 | 0]: 1 1 250 [0x210403d0]: 00 00 00 00 00 00 00 00 | [0x21041e80 | 4]: 1 1 251 [0x210403d8]: 00 00 00 00 00 00 00 00 | [0x21041e81 | 0]: 1 1 252 [0x210403e0]: 00 00 00 00 00 00 00 00 | [0x21041e81 | 4]: 1 1 253 [0x210403e8]: 00 00 00 00 00 00 00 00 | [0x21041e82 | 0]: 1 1 254 [0x210403f0]: 00 00 00 00 00 00 00 00 | [0x21041e82 | 4]: 1 1 255 [0x210403f8]: 40 1e 04 21 05 07 00 80 | [0x21041e83 | 0]: 2 2 256 [0x21040400]: 00 00 00 00 00 00 00 00 | [0x21041e83 | 4]: 0 0 257 [0x21040408]: 00 00 00 00 00 00 00 00 | [0x21041e84 | 0]: 0 0 258 [0x21040410]: 00 00 00 00 f8 03 04 21 | [0x21041e84 | 4]: 0 2 259 [0x21040418]: 05 8b 06 00 [00] 00 00 00 | [0x21041e85 | 0]: 2 [3] 260 [0x21040420]: 00 00 00 00 00 00 00 00 | [0x21041e85 | 4]: 3 3 261 [0x21040428]: 00 00 00 00 00 00 00 00 | [0x21041e86 | 0]: 3 3 262 [0x21040430]: 14 04 04 21 00 84 06 00 | [0x21041e86 | 4]: 2 2 263 [0x21040438]: 00 00 00 00 00 00 00 00 | [0x21041e87 | 0]: 3 3 264 [0x21040440]: 00 00 00 00 00 00 00 00 | [0x21041e87 | 4]: 3 3 265 [0x21040448]: 00 00 00 00 00 00 00 00 | [0x21041e88 | 0]: 3 3 266 [0x21040450]: 00 00 00 00 00 00 00 00 | [0x21041e88 | 4]: 3 3 267 [0x21040458]: 00 00 00 00 00 00 00 00 | [0x21041e89 | 0]: 3 3 268 [0x21040460]: 00 00 00 00 00 00 00 00 | [0x21041e89 | 4]: 3 3 269 [0x21040468]: 00 00 00 00 00 00 00 00 | [0x21041e8a | 0]: 3 3 270 [0x21040470]: 00 00 00 00 00 00 00 00 | [0x21041e8a | 4]: 3 3 271 [0x21040478]: 00 00 00 00 00 00 00 00 | [0x21041e8b | 0]: 3 3 272 [0x21040480]: 00 00 00 00 00 00 00 00 | [0x21041e8b | 4]: 3 3 273 [0x21040488]: 00 00 00 00 00 00 00 00 | [0x21041e8c | 0]: 3 3 274 [0x21040490]: 00 00 00 00 00 00 00 00 | [0x21041e8c | 4]: 3 3 275 [0x21040498]: 00 00 00 00 00 00 00 00 | [0x21041e8d | 0]: 3 3 276[ERR][TestLmsTsk]***** Kernel Address Sanitizer Error Detected End ***** 277str[ 0]=0x 0 278######LmsTestUseAfterFree stop ###### 279``` 280 281输出的关键信息包括: 282 283- 错误类型: 284 - Heap buffer overflow堆内存越界 285 - Use after free 释放后使用 286 287- 错误操作: 288 - Illegal Read非法读 289 - Illegal Write非法写 290 - Illegal Double free重复释放 291 292- 上下文: 293 - 当前任务信息(taskName, taskId) 294 - 回溯栈(backtrace) 295 296- 出错地址的内存信息: 297 - 内存的值、及对应影子内存的值 298 - 内存地址:内存值| [影子内存地址 | 影子内存字节内偏移]:影子内存值 299 - 影子内存值:0(可读可写)、3(已释放)、2(红区)、1(填充值) 300