当前位置:首页 > 汽车电子 > 汽车电子
[导读]车规MCU里指针越界、栈溢出、野指针写外设,光靠代码审查救不回来——破坏已经发生了。MPU的价值是把“静默写坏”变成“硬件立即拦下”:CPU发起访问前查区域权限,不匹配直接MemManage Fault,不会执行那条非法读写。


车规MCU里指针越界、栈溢出、野指针写外设,光靠代码审查救不回来——破坏已经发生了。MPU的价值是把“静默写坏”变成“硬件立即拦下”:CPU发起访问前查区域权限,不匹配直接MemManage Fault,不会执行那条非法读写。

一、区域规划先于代码

Cortex-M的MPU区域数有限(通常8或16),先画内存地图再配:

Flash代码区:特权/用户只读、可执行

安全任务RAM:仅本任务读写、不可执行

外设寄存器区:仅特权访问、XN=1

栈底guard:无访问权限

未使用地址:默认拒绝

区域大小必须是2的幂,基地址按大小对齐,否则MPU会静默把基址截低。

二、配置流程:先关后开

改MPU必须先把CTRL.EN清0,配完再开,最后DSB+ISB生效:

void mpu_init(void){

   MPU->CTRL = 0;                       // 1.关MPU


   // Region0: Flash 只读可执行

   MPU->RNR = 0;

   MPU->RBAR = 0x08000000U | MPU_RBAR_VALID_Msk;

   MPU->RASR = MPU_RASR_ENABLE_Msk

             | (15 << MPU_RASR_SIZE_Pos)          // 32KB

             | MPU_RASR_AP_PRIV_RO_UNPRIV_RO

             | MPU_RASR_TEX_0;                    // Normal


   // Region1: 安全任务栈区 RW、不可执行

   MPU->RNR = 1;

   MPU->RBAR = TASK_RAM_BASE | MPU_RBAR_VALID_Msk;

   MPU->RASR = MPU_RASR_ENABLE_Msk

             | (12 << MPU_RASR_SIZE_Pos)          // 4KB

             | MPU_RASR_AP_PRIV_RW_UNPRIV_NO

             | MPU_RASR_XN_Msk;


   // Region2: 外设区 仅特权、设备属性、不可执行

   MPU->RNR = 2;

   MPU->RBAR = 0x40000000U | MPU_RBAR_VALID_Msk;

   MPU->RASR = MPU_RASR_ENABLE_Msk

             | (29 << MPU_RASR_SIZE_Pos)          // 512MB

             | MPU_RASR_AP_PRIV_RW_UNPRIV_NO

             | MPU_RASR_XN_Msk

             | MPU_RASR_TEX_0;                    // Device/Strongly-ordered


   // Region3: 栈底guard 无访问

   MPU->RNR = 3;

   MPU->RBAR = STACK_GUARD_BASE | MPU_RBAR_VALID_Msk;

   MPU->RASR = MPU_RASR_ENABLE_Msk

             | (7 << MPU_RASR_SIZE_Pos)           // 256B

             | MPU_RASR_AP_PRIV_NO_UNPRIV_NO

             | MPU_RASR_XN_Msk;


   MPU->CTRL = MPU_CTRL_ENABLE_Msk

             | MPU_CTRL_PRIVDEFENA_Msk;           // 特权默认背景区

   __DSB(); __ISB();

}

PRIVDEFENA开着方便内核/ISR访问未显式配置区;ASIL-D严配可关掉,全部显式建区。

三、栈guard别太小

guard区大小按“最大中断嵌套深度+最深调用”的栈消耗留余量,32~256字节都常见。

链接脚本里栈底留一块:

__attribute__((aligned(256)))

static uint8_t brake_task_stack[1024 + 256];

#define STACK_GUARD_BASE  ((uint32_t)brake_task_stack)

栈一越界踩到guard,MemManage立刻触发,不会覆盖隔壁任务的校准变量。

四、外设区只给特权

刹车执行器、电机PWM、安全状态寄存器,非安全任务不该直接写。配成UNPRIV_NO后,用户态任务碰外设寄存器直接fault,只能通过系统调用/Trusted Function让OS代理写。

注意外设区TEX/C/B按芯片手册设成设备/强序,别开Cache,否则读写合并会出事。

五、MemManage处理:别“修复后继续”

车规场景下MPU触发=系统已偏离预期,正确动作是记录现场→进安全态→上报,不是改指针继续跑:

void MemManage_Handler(void){

   uint32_t cfsr = SCB->CFSR;

   uint32_t far  = (cfsr & SCB_CFSR_MMARVALID_Msk)

                    ? SCB->MMFAR : 0;

   fault_log_store(cfsr, far, __builtin_return_address(0));

   safe_state_enter();        // 关PWM/输出、置故障标志

   wdog_trigger_reset();      // 或等外部看门狗

   for(;;);

}

关键:

故障处理代码自身所在Flash区配成特权只读,不能被篡改

MMFAR有效位先判,避免读无效地址

别在handler里做复杂恢复,安全状态优先

六、RTOS多任务切换

每任务不同栈/数据区时,调度器切任务要重配MPU区域(或AUTOSAR OS由ProtectionHook接管)。

切换顺序:存当前→改RBAR/RASR→DSB/ISB→切栈。区域不够用时用重叠优先级:大区域默认拒绝,小区域按任务放开。

七、车规落坑

区域不对齐:以为配了0x20004000/48KB,实际基址被截到0x20000000

guard太小:中断嵌套一深直接跳过guard不触发

XN没开:栈区可执行,漏洞可被注入

PRIVDEFENA滥用:特权ISR写飞也无人拦截

非可信任务拿特权:CONTROL.nPRIV没切回去

只配MPU不处理异常:fault进Default_Handler等于没防

DMA越界MPU管不到:Cortex-M MPU只管CPU访问,DMA要走总线保护/SRI

MPU不是加密也不是沙箱,它干的事很明确:把内存访问策略钉成硬件规则,越界即异常、外设即特权、栈底即红线。车规C项目里配合静态分配、禁止malloc、栈水印,再把MemManage接进安全状态机,ISO 26262要求的“故障可发现、破坏可阻止”才算落地。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭