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