更新时间:2021-07-08 GMT+08:00
分享

内存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使能内存检测

错误码定义见错误码简介。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”的值,查看函数的嵌套调用。

相关文档