
# 内存size检查
#### 使用场景
memset和memcpy操作动态内存，发生越界踩内存问题。
#### 功能说明
对于memset和memcpy操作，当入参为动态内存节点时，增加对内存节点实际大小与入参指定大小的检查，若指定大小大于节点实际大小时，输出error信息，并且取消该次memset或memcpy操作，所以能够防止操作越界。动态内存越界场景下，可开启该功能定位问题。
| 接口名                  | 描述               |
|:---|:---|
| LOS_MemCheckLevelSet | 设置内存检查级别         |
| LOS_MemCheckLevelGet | 获取内存检查级别         |
| LOS_MemNodeSizeCheck | 获取指定内存块的总大小和可用大小 |
   
#### 错误码
| 序号 | 定义                             | 实际数值       | 描述                            | 参考解决方案                                                     |
|:---|:---|:---|:---|:---|
| 1  | LOS_ERRNO_MEMCHECK_PARA_NULL   | 0x02000101 | LOS_MemNodeSizeCheck的入参中存在空指针 | 传入有效指针                                                     |
| 2  | LOS_ERRNO_MEMCHECK_OUTSIDE     | 0x02000102 | 内存地址不在合法范围内                   | 输入内存地址本身不在内存管理范围之内，不做处理                                    |
| 3  | LOS_ERRNO_MEMCHECK_NO_HEAD     | 0x02000103 | 内存地址已经被释放或者是野指针               | 检查传入的内存地址，保证该地址是有效地址                                       |
| 4  | LOS_ERRNO_MEMCHECK_WRONG_LEVEL | 0x02000104 | 内存检测等级不合法                     | 通过LOS_MemCheckLevelGet检查等级，并通过LOS_MemCheckLevelSet来配置合法的等级 |
| 5  | LOS_ERRNO_MEMCHECK_DISABLED    | 0x02000105 | 内存检测被关闭                       | 通过LOS_MemCheckLevelSet使能内存检测                               |
   
![](https://support.huaweicloud.com/maintenance-LiteOS/public_sys-resources/notice_3.0-zh-cn.png)
错误码定义见[错误码简介](https://support.huaweicloud.com/maintenance-LiteOS/zh-cn_topic_0175230332.html#ZH-CN_TOPIC_0175230332__section29852515161)。8\~15位的所属模块为动态内存模块，值为0x01。
#### 使用方法
通过make menuconfig打开内存size检查：
- 目前只有bestfit内存管理算法支持该功能，所以需要使能LOSCFG_KERNEL_MEM_BESTFIT。
  ```
  Kernel ---> Memory Management ---> Dynamic Memory Management Algorithm ---> bestfit
  ```
  
- 同时该功能依赖于LOSCFG_BASE_MEM_NODE_SIZE_CHECK，该宏开关可以通过在菜单项中开启"Enable size check or not"使能。
  ```
  Debug  ---> Enable a Debug Version ---> Enable MEM Debug ---> Enable size check or not
  ```
  
 
#### 注意事项
开启该功能后，memset和memcpy性能下降，建议仅在需要定位越界问题时开启，默认关闭。
#### 内存越界问题定位实例
通过构造超出内存长度的memset和memcpy操作，构造越界踩内存问题，构造代码如下：
```
VOID test(VOID)
{
    UINT32 size = 0x100;
    VOID *poolAddr1 = LOS_MemAlloc((VOID *)OS_SYS_MEM_ADDR, size);
    if (poolAddr1 == NULL) {
        PRINTK("malloc poolAddr1 failed\n");
        return;
    } else {
        PRINTK("malloc poolAddr1 %p successed\n", poolAddr1);
    }
    VOID *poolAddr2 = LOS_MemAlloc((VOID *)OS_SYS_MEM_ADDR, size);
    if (poolAddr2 == NULL) {
        PRINTK("malloc poolAddr2 failed\n");
        return;
    } else {
        PRINTK("malloc poolAddr2 %p successed\n", poolAddr2);
    }
    LOS_MemCheckLevelSet(LOS_MEM_CHECK_LEVEL_LOW);      // 开启对memset和memcpy的长度检测
    PRINTK("memset poolAddr1 overflow\n"); 
    memset(poolAddr1, 0x10, size * 2);                  // 超出长度的memset
    PRINTK("memset poolAddr1\n"); 
    memset(poolAddr1, 0x10, size);                      // 合理长度的memset
    PRINTK("memcpy poolAddr2 overflow\n"); 
    memcpy(poolAddr2, poolAddr1, size * 2);            // 超出长度的memcpy
    PRINTK("memcpy poolAddr2\n"); 
    memcpy(poolAddr2, poolAddr1, size);                // 合理长度的memcpy
    LOS_MemCheckLevelSet(LOS_MEM_CHECK_LEVEL_DISABLE); // 关闭对memset和memcpy的长度检测
    LOS_MemFree((VOID *)OS_SYS_MEM_ADDR, (VOID *)poolAddr1);
    LOS_MemFree((VOID *)OS_SYS_MEM_ADDR, (VOID *)poolAddr2);
    return 0;
}
```
log：
```
malloc poolAddr1 0x80349514 successed
malloc poolAddr2 0x80349624 successed
LOS_MemCheckLevelSet: LOS_MEM_CHECK_LEVEL_LOW
memset poolAddr1 overflow
[ERR] ---------------------------------------------
memset: dst inode availSize is not enough availSize = 0x100, memcpy length = 0x200
runTask->taskName = osMain
runTask->taskId = 64
*******backtrace begin*******
traceback 0 -- lr = 0x80209798    fp = 0x802c6930
traceback 1 -- lr = 0x80210fc4    fp = 0x802c6954
traceback 2 -- lr = 0x8020194c    fp = 0x802c6994
traceback 3 -- lr = 0x80201448    fp = 0x802c699c
traceback 4 -- lr = 0x802012fc    fp = 0x0
[ERR] ---------------------------------------------
memset poolAddr1
memcpy poolAddr2 overflow
[ERR] ---------------------------------------------
memcpy: dst inode availSize is not enough availSize = 0x100, memcpy length = 0x200
runTask->taskName = osMain
runTask->taskId = 64
*******backtrace begin*******
traceback 0 -- lr = 0x80209798    fp = 0x802c6930
traceback 1 -- lr = 0x8020dbc4    fp = 0x802c6954
traceback 2 -- lr = 0x8020194c    fp = 0x802c6994
traceback 3 -- lr = 0x80201448    fp = 0x802c699c
traceback 4 -- lr = 0x802012fc    fp = 0x0
[ERR] ---------------------------------------------
memcpy poolAddr2
```
由于开启size检测，非法的memset和memcpy操作被取消，输出error信息。"runTask-\>taskName = osMain"显示了该非法操作发生在 osMain函数中，并打印寄存器lr和fp的值。此时可以打开编译后生成的 .asm反汇编文件，默认生成在Huawei_LiteOS/out/\<platform\>目录下，其中的platform为具体的平台名，通过对比"寄存器lr"的值，查看函数的嵌套调用。
