打印
[学习笔记]

UCOS III通过M3内核MPU模块实现任务栈溢出检测

[复制链接]
2114|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
硬件环境: AC7811通用开发板  ATC-LINK
软件环境:keil 5.23

前段时间有客户提到AC7811怎么实现栈溢出检测,便想到使用MPU模块在栈顶设置一段禁用区,以在栈使用溢出时访问到禁用区,产生memory manage fault,从而进行下一步的处理。再便想到可以通过该功能来实现带操作系统的用户栈溢出检测功能。

MPU的配置:
MPU可以依据实际的使用情况灵活配置,我这里为了简便,便创建了8个任务,每个任务都使用512字节的栈空间,这样便于我配置MPU。

MPU的配置如下:
    if ((MPU->TYPE & 0x800) == 0)   ///<do not support MPU
    {
        return;
    }
   
    MPU->CTRL = 0x04;  ///<enable backround region, disable MPU
   
    MPU->RNR = 0;   ///<select region 0
    MPU->RBAR = 0x2000E000;
    MPU->RASR = 0x07000017;
   
    MPU->RNR = 1;   ///<select region 1
    MPU->RBAR = 0x2000E000;
    MPU->RASR = 0x03000017;
   
    MPU->CTRL |= 1;
   
   
    SCB->SHCSR |= 1 << 16;  ///<enable memory manage interrupt
通过sct文件将8个任务的栈空间都存放再0x2000E000开始的位置,这里配置了两个region,启用了背景region。region0 用于配置该区域为只读。region1配置该区域可读可写,两个region重叠,因此该区域最终是可读可写的。

任务切换:
void  App_OS_TaskSwHook (void)
{
    if (OSTCBHighRdyPtr->ExtPtr != 0)
    {
        MPU->RNR = 1;   ///<select region 0
        MPU->RBAR = 0x2000E000;
        MPU->RASR = *((uint32_t *)OSTCBHighRdyPtr->ExtPtr);
        
    }        
}


进行任务切换时,在任务切换hook中,重新设置region1,主要为了修改子region。按内核手册的说法,一个region分为8个子region,可以通过SRD寄存器除能响应的子region。因此我们每要切换一个任务时,应该除能除此任务外的其他任务栈的写入权限。这里的配置参数我们通过OS提供的OSTCBHighRdyPtr->ExtPtr参数传入。
static const uint32_t AppTask1Ext = 0x0300FD17;
static const uint32_t AppTask2Ext = 0x0300FB17;
static const uint32_t AppTask3Ext = 0x0300F717;
static const uint32_t AppTask4Ext = 0x0300EF17;
static const uint32_t AppTask5Ext = 0x0300DF17;
static const uint32_t AppTask6Ext = 0x0300BF17;
static const uint32_t AppTask7Ext = 0x03007F17;
该参数在创建任务的时候传入,每个任务只使能自己的子region,除能其他任务的子region。这样其他子region无效,对应的其他栈便适用region0的规则,变成了只读(这里设置只读是因为OS的STAT任务有对各任务栈使用情况进行统计,如果直接禁止访问会导致该任务无法运行)。
对于操作系统自带的任务,我们不纳入管理范围,通过OSTCBHighRdyPtr->ExtPtr的非零判断,可以排除操作系统自己的任务。
最后,在OS进行任务切换时,会同时使用到当前任务栈和就绪的最高优先级任务栈,为了解决此时同时使用两个栈的问题,我在这里暂时关闭了MPU:
OS_CPU_PendSVHandler
    CPSID   I                                                   ; Prevent interruption during context switch
;add by jason for mpu
    LDR     R1, =0xE000ED94
    LDR     R0, =0x00000004
    STR     R0, [R1]
;end
    MRS     R0, PSP                                             ; PSP is process stack pointer
    CBZ     R0, OS_CPU_PendSVHandler_nosave                     ; Skip register save the first time

    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack
    STM     R0, {R4-R11}

    LDR     R1, =OSTCBCurPtr                                    ; OSTCBCurPtr->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                                            ; R0 is SP of process being switched out

                                                                ; At this point, entire context of process has been saved
OS_CPU_PendSVHandler_nosave
    PUSH    {R14}                                               ; Save LR exc_return value
    LDR     R0, =OSTaskSwHook                                   ; OSTaskSwHook();
    BLX     R0
    POP     {R14}

    LDR     R0, =OSPrioCur                                      ; OSPrioCur   = OSPrioHighRdy;
    LDR     R1, =OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    LDR     R0, =OSTCBCurPtr                                    ; OSTCBCurPtr = OSTCBHighRdyPtr;
    LDR     R1, =OSTCBHighRdyPtr
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdyPtr->StkPtr;
    LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                                             ; Load PSP with new process SP
    ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack
    CPSIE   I
;add by jason for mpu
    LDR     R1, =0xE000ED94
    LDR     R0, =0x00000005
    STR     R0, [R1]
;end   
    BX      LR                                                  ; Exception return will restore remaining context

    END
最后就是核心的异常处理了:
void MemManage_Handler(void)
{
    OS_ERR      err;
    printf("%s is overflow\r\n", OSTCBCurPtr->NamePtr);
    OS_RdyListRemove(OSTCBCurPtr);
    OSTaskDel(OSTCBCurPtr, &err);
    OSSched();
    //while(1);
}
这里我在发生栈溢出后,移除并删除了当前任务,同时进行新的任务调度(这里对UCOS III的运行还不熟悉,所以这里使用上比较粗暴直接了,不确定是否会有其他问题)。

测试:
测试的方法也很简单,我在task2中申请了一个大于512字节的局部数组,然后对超过512字节后面的位置进行写入操作。此时的访问应当会落在其他任务的栈空间上,因为其他任务栈空间已经配置只读,所以会产生memmanage fault。
static  void  AppTask2 (void *p_arg)
{
    OS_ERR      err;
    uint32_t testdata[130];  
    p_arg = p_arg;
    while(1)
    {
        printf("Task2\r\n");
        testdata[130] = testdata[130];
        OSTimeDlyHMSM(0, 0, 1, 0,
                      OS_OPT_TIME_HMSM_STRICT,
                      &err);
    }
}
测试log输出如下:
似乎不需要额外的执行testdata[130] = testdata[130];因为printf打印函数运行的时候已经栈溢出了,所以printf函数一旦访问栈空间,就会产生memmanage fault。接着我们删除了task2.因此其他任务还可以照常执行。
Creating Application Tasks...
Creating Application Events...
Task1
App Task2 is overflow
Task3
Task4
Task5
Task6
Task7
Task1
Task3
Task4
Task5
Task6
Task7


总结:这功能想了想感觉实际用途不是很大,但在设计之初,我们总会发生一些意外的栈溢出问题,在OS下会很难定位到具体是哪个任务哪个函数导致了栈溢出。这个方法或许会比较方便大家定位栈溢出问题?当然,这里面还有很多不成熟的地方,比如第一个任务发生溢出的话,溢出的位置小于0x2000E000,不在只读范围内,因此可能还需要在这个区域额外设置一个只读的临界区。

最后附上测试用的代码:
UCOSIII_sample.rar (2.37 MB)

使用特权

评论回复

相关帖子

沙发
caigang13| | 2022-3-1 20:44 | 只看该作者
感谢分享,还在用ucosII

使用特权

评论回复
板凳
chenjun89| | 2022-3-2 07:48 | 只看该作者
表示还在用ucosII的举个手。

使用特权

评论回复
地板
JasonLee27|  楼主 | 2022-3-3 10:17 | 只看该作者
chenjun89 发表于 2022-3-2 07:48
表示还在用ucosII的举个手。

只是在UCOS III上做了个测试,其他系统都是可以做的,UCOS III主要是把中断纳入了任务管理

使用特权

评论回复
5
tpgf| | 2022-4-1 10:42 | 只看该作者
这个系统在哪里可以下载到呢

使用特权

评论回复
6
heimaojingzhang| | 2022-4-1 10:52 | 只看该作者
会不会占用资源太多呢

使用特权

评论回复
7
keaibukelian| | 2022-4-1 11:10 | 只看该作者
我还是用的老系统

使用特权

评论回复
8
labasi| | 2022-4-1 11:18 | 只看该作者
这两个系统的差别是什么呢

使用特权

评论回复
9
paotangsan| | 2022-4-1 11:25 | 只看该作者
现在这个运行稳定吗

使用特权

评论回复
10
renzheshengui| | 2022-4-1 11:31 | 只看该作者
会不会读硬件要求更高啊

使用特权

评论回复
11
biechedan| | 2022-4-1 12:41 | 只看该作者
这个可行吗

使用特权

评论回复
12
wengh2016| | 2022-4-1 12:49 | 只看该作者
栈溢出不是都会报错吗

使用特权

评论回复
13
updownq| | 2022-4-1 13:12 | 只看该作者
数组溢出有检测的方法吗

使用特权

评论回复
14
lihuami| | 2022-4-1 13:36 | 只看该作者
任务栈溢出检测怎么实现的呢

使用特权

评论回复
15
mnynt121| | 2022-4-1 14:03 | 只看该作者
必须通过MPU模块吗

使用特权

评论回复
16
xiaoyaozt| | 2022-4-1 14:40 | 只看该作者
会不会 hard fault

使用特权

评论回复
17
foxsbig| | 2022-4-9 16:16 | 只看该作者
貌似会报错吧

使用特权

评论回复
18
guijial511| | 2022-4-11 20:29 | 只看该作者
这样也行?稳定吗?

使用特权

评论回复
19
yangxiaor520| | 2022-4-12 08:06 | 只看该作者
这样做可靠吗?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

66

主题

415

帖子

10

粉丝