• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.gn272
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