一、 上下文切换:地址空间与寄存器的双重切换
OP-TEE的上下文切换分为两个层面:线程上下文切换和TA地址空间切换,两者互相配合,保证执行的正确性与隔离性。
线程寄存器上下文切换
线程挂起和恢复时,会完整保存/恢复所有通用寄存器、程序状态寄存器、栈指针,保证恢复后能精确回到挂起前的执行状态,和异常切换的上下文保存机制一致。由于线程是静态分配的,每个线程都有独立的内核栈,切换时只需要修改栈指针和恢复寄存器,开销非常小。
TA地址空间切换
如果前后两个线程运行的是不同的TA,切换时还会更换用户态页表:
- 写入TTBR0_EL1寄存器,指向新TA的用户态页表基地址;
- 更新CONTEXTIDR_EL1寄存器,设置新的ASID地址空间编号;
- 执行指令同步屏障,刷新TLB缓存,保证地址映射生效。
通过更换页表基地址和ASID,实现了不同TA之间地址空间的完全隔离——即使两个TA的虚拟地址完全相同,映射的物理内存也完全不同,不会互相访问到对方的数据。源码中该逻辑由core_mmu_set_user_map()函数实现,位于core/arch/arm/mm/core_mmu.c。
1.1 中断处理:两类中断的不同处理逻辑
OP-TEE运行过程中会遇到两类中断,处理方式完全不同:
- 外部中断(Foreign Interrupt):来自非安全世界的中断,比如普通外设的IRQ。这类中断发生时,OP-TEE会立即暂停当前线程,保存上下文,通过SMC切回非安全世界,由Linux内核处理中断;等中断处理完成、Linux线程重新切入TEE时,再恢复线程继续执行。这种设计保证了普通世界的中断响应延迟,也避免了安全世界处理非安全中断的安全风险。
- 本地安全中断(Native Interrupt):安全外设触发的中断,比如硬件加密引擎完成中断、安全定时器中断。这类中断由OP-TEE内核直接处理,不会切出安全世界,处理完成后回到被打断的线程继续执行。
1.2 并发与同步原语
虽然没有主动调度器,但多线程并发仍然存在:多个CPU核心同时运行不同线程、同一个CPU上不同线程交替切入切出,都需要同步机制保证资源安全。
OP-TEE内核提供了标准的同步原语:
- 互斥锁(mutex):保护共享资源的互斥访问,比如内核全局数据结构、硬件加密引擎;
- 自旋锁(spinlock):多核场景下的短时间临界区保护,关中断加锁,避免死锁;
- 条件变量(condvar):线程间的事件等待与通知,用于多线程协作场景。
所有同步原语都严格考虑了中断安全和多核安全,保证在安全世界的并发场景下不会出现竞态漏洞。
二、内存管理与安全隔离机制
内存安全是TEE整个安全体系的基石——如果内存隔离被突破,攻击者可以直接读取密钥、篡改代码,所有上层安全防护都会失效。OP-TEE采用「硬件隔离+软件隔离」的双重防护体系,从总线级到页表级再到应用级,层层递进实现内存安全。
2.1 硬件级根基:TZASC的物理内存隔离
所有内存防护的第一层,是我们第一篇讲过的TrustZone地址空间控制器(TZASC)。OP-TEE使用的所有物理内存,都被TZASC配置为「仅安全态可访问」:
- 非安全世界的任何总线访问,都会被TZASC直接拒绝,返回总线错误;
- 哪怕Linux内核被完全攻破、拿到EL1最高权限,也无法读写安全内存的一个字节;
- 安全内存的大小和起始地址在编译时确定,由平台配置,运行时无法修改。
这是最底层、最坚固的防护,是所有软件内存安全的前提。你可以做一个简单验证:在U-Boot(非安全态)中读取0xe1000000地址,会直接触发访问错误,就是TZASC硬件拦截的效果。
2.2 MMU页表隔离:内核与用户态、TA与TA的双重边界
在硬件隔离的基础上,OP-TEE通过MMU页表进一步实现软件层面的权限隔离,分为两个维度:
维度1:内核态与用户态隔离
OP-TEE使用两级页表基址寄存器:
- TTBR1_EL1:内核空间页表,全局唯一,映射所有内核代码、数据、堆、设备寄存器;
- TTBR0_EL1:用户空间页表,每个TA独立一份,映射单个TA的代码、数据、栈、堆。
权限配置上严格遵循最小权限原则:
- 内核代码段:只读、可执行、仅EL1可访问;
- 内核数据段:读写、不可执行、仅EL1可访问;
- TA代码段:只读、可执行、EL0可访问;
- TA数据段和堆:读写、不可执行、EL0可访问。
TA运行在Secure EL0,无法访问内核空间的任何地址,也无法修改页表配置;所有对内核资源的访问,都必须通过系统调用陷入内核,由内核校验权限后执行。这种设计和Linux的用户态/内核态隔离完全一致,但因为TEE的攻击面更小、系统调用更少,安全性更高。
维度2:TA与TA之间的地址空间隔离
每个TA都有独立的TTBR0_EL1页表,拥有完全独立的虚拟地址空间。切换TA时,内核会更换TTBR0基地址和ASID编号,当前TA完全无法访问其他TA的内存,哪怕是同一块物理内存,也不会被映射到当前TA的虚拟地址空间中。
这种隔离的安全意义在于:即使某个TA存在漏洞被攻击者控制,攻击者也只能访问当前TA的内存,无法窃取其他TA的密钥和数据,更无法攻破内核,攻击影响被严格限制在单个TA范围内。
2.3 内核堆管理:静态池下的安全分配
和普通操作系统不同,OP-TEE的内存是固定大小的静态池,没有虚拟内存、没有swap交换,所有内存分配都在预分配的安全内存池中进行。
堆内存管理的核心特点:
- 静态预留:编译时确定内核堆的总大小,启动后一次性预留,不会动态扩容;
- 分配算法:采用内存池+块分配算法,兼顾分配速度和内存利用率,避免内存碎片;
- 安全释放:释放内存时,自动清零整个内存块,防止敏感数据残留在内存中被后续分配的代码读取;
- 越界检测:堆块前后设置防护金丝雀,检测缓冲区溢出,一旦检测到越界写入直接触发异常终止。
对于TEE开发来说,内存是极其宝贵的资源:安全内存总大小通常只有几MB到几十MB,内核、线程栈、TA代码、TA堆都要从里面分配。编写TA时必须严格控制内存占用,避免内存泄漏,否则很容易耗尽安全内存导致后续TA加载失败。
2.4 共享内存机制:零拷贝传输的安全设计
CA和TA之间经常需要传输大量数据,比如加密文件、视频帧,如果每次都做内存拷贝,性能会非常差。OP-TEE提供了共享内存机制,实现零拷贝数据传输,但在设计上做了严格的安全约束。
共享内存的工作流程
- REE侧的CA分配一块物理连续的内存,注册为共享内存;
- 通过驱动通知TEE,TEE将这块物理内存映射到自己的虚拟地址空间;
- CA和TA可以直接读写同一块物理内存,不需要拷贝;
- 使用完成后,双方解除映射,释放内存。
关键安全设计
共享内存是跨世界的交互通道,也是重要的攻击面,因此做了多重防护:
- 权限隔离:共享内存只能映射到TA用户空间,不能映射到内核空间,防止共享内存的漏洞影响内核;
- 范围校验:TA访问共享内存时,内核会严格校验地址范围,防止通过越界访问渗透到安全内存内部;
- 并发安全:共享内存的读写由双方协商同步,TEE侧使用数据前必须做完整性校验,防止REE侧在TEE处理过程中篡改数据(Time-of-check to time-of-use攻击);
- 敏感数据不落地:密钥等最高敏感数据绝对不能放在共享内存中,必须在安全内存内部处理。
2.5 内存安全加固:多层防护抵御漏洞攻击
为了抵御内存破坏类漏洞,OP-TEE内核内置了多重内存安全加固机制:
- 栈保护(Stack Canary):函数栈帧中插入金丝雀值,函数返回前校验,检测栈溢出攻击;
- 不可执行内存(NX):所有数据段、堆、栈都设置为不可执行,防止代码注入攻击;
- 地址空间随机化(ASLR):TA加载地址、堆地址、栈地址随机化,增加漏洞利用难度;
- 释放后清零:所有堆内存释放时自动清零,防止敏感数据残留和释放后重用漏洞;
- 页表权限固化:内核页表运行时设为只读,防止攻击者篡改页表权限提升权限。
这些加固机制和普通操作系统的安全防护类似,但因为TEE的代码量更小、攻击面更窄,防护的有效性更高。

966

被折叠的 条评论
为什么被折叠?



