ARM TrustZone-M中安全世界变量为何遮蔽非安全世界同名变量
问题根因
这个问题和TrustZone-M硬件隔离失效无关,本质是C语言符号定义不规范+TrustZone-M工程链接逻辑的认知偏差共同导致的,核心问题点如下:
1. 混淆了运行时隔离边界和编译链接边界
你之前基于TrustZone-A、SGX形成的「两个世界完全隔离」认知只适用于芯片运行时的硬件访问控制阶段:TrustZone-M确实会通过SAU/IDAU配置,拦截非安全状态对安全内存、安全外设的访问。
但在编译链接阶段,启用TrustZone-M的STM32L5工程中,安全世界编译完成后会生成一个供非安全世界调用NSC接口的导入库,非安全世界编译时必须链接这个库才能调用安全侧提供的接口——也就是说,非安全链接阶段是能看到安全世界导出的所有全局符号的,两个世界的符号空间在这个阶段不是完全隔离的。
2. 头文件错误定义全局变量,留下符号绑定漏洞
你在两个世界的main.h中直接写ringbuf_t foo;属于全局变量的试探性定义(弱符号),而非extern形式的纯声明。按照C语言链接规则,弱符号会被链接过程中遇到的第一个同名强符号/全局符号覆盖。
安全世界定义的foo是全局非静态符号,会被默认放到给非安全侧的导出库中,地址自然分配在0x3开头的安全SRAM区间。非安全侧链接时,扫描到这个来自安全库的同名全局符号优先级高于自身生成的弱符号foo,就会直接把非安全代码里所有对foo的引用绑定到安全侧的foo地址上。
这完全匹配你观察到的现象:
- 非安全代码访问0x3开头的安全地址时,会被运行时的TrustZone访问控制拦截,写入操作直接被总线丢弃,表现为写入失效;
- 注释掉安全侧的
foo定义后,安全导出库中不再存在同名符号,非安全链接器只能绑定自身编译生成的foo,地址就正确落到0x2开头的非安全SRAM区间,访问恢复正常。
修复步骤
按以下顺序修改即可彻底解决问题:
- 修正全局变量的定义规范
两个世界的main.h头文件中,将ringbuf_t foo;修改为extern ringbuf_t foo;,仅做变量声明;在各自世界的main.c顶部添加ringbuf_t foo;完成变量的实际定义,从根源上避免弱符号被意外覆盖。 - 收敛安全世界的符号导出范围
安全侧所有不需要暴露给非安全世界的全局变量、内部函数,统一加static修饰将作用域限定在当前编译单元内;也可以在编译选项中添加-fvisibility=hidden(GCC/Arm Clang均支持),仅给需要对外暴露的NSC接口添加__attribute__((visibility("default")))标记,避免内部符号泄漏到非安全侧的链接过程。 - 校验NSC导出列表
编译完成后检查安全侧生成的NSC导出头文件、导入库的符号表,确认仅包含你设计的非安全可调用接口,不存在多余的内部变量、函数符号,从源头避免符号污染。
内容的提问来源于stack exchange,提问作者iMrFelix
相关产品推荐
相关产品推荐

