4. 共享内存核心概念
在 OP-TEE 的可信执行环境(TEE)体系中,普通世界(Normal World,即 Linux/Android 侧)与安全世界(Secure World,即 TEE 侧)运行在相互隔离的两个执行域中。二者拥有独立的地址空间、页表与内存管理单元配置,因此不能像同一进程内的两个线程那样直接通过指针访问对方的数据。共享内存(Shared Memory)正是为解决这一跨世界数据交换问题而设计的核心机制:它是一块由双方协商后共同映射、物理上同一、逻辑上各自可见的内存区域,用于在 REE 与 TEE 之间传递命令参数、大块数据、会话上下文与执行结果。
一、共享内存的定义与作用
共享内存的本质是“同一段物理内存在两个世界中分别建立映射”。普通世界通过其虚拟地址访问,安全世界通过自己的虚拟地址访问,但底层指向相同的物理页。OP-TEE 中,共享内存通常由普通世界侧(如 OP-TEE Client 库)分配并注册,再通过 SMC(Secure Monitor Call)指令把物理地址、大小和方向等元数据传递给安全世界,由 TEE 内核完成映射与校验。
它的主要作用包括:
- 参数传递:把 TA(Trusted Application)调用所需的输入缓冲区、输出缓冲区地址告知 TEE。
- 大数据搬运:寄存器只能传少量字长数据,大块数据(如加解密明文、密钥材料、图像数据)必须借助共享内存。
- 零拷贝优化:避免在 REE 与 TEE 之间反复复制数据,降低延迟与内存带宽消耗。
- 会话与上下文承载:承载会话对象、操作句柄等需要跨多次调用保持的数据结构。
二、共享内存的分类
按不同维度,OP-TEE 中的共享内存可以划分为若干类型,理解分类有助于正确选择 API 与排查映射问题。
| 分类维度 | 类型 | 典型用途与特点 |
|---|---|---|
| 按安全属性 | 非安全共享内存 | 由普通世界分配,位于非安全 DRAM,TEE 侧以只读或读写方式映射,最常见 |
| 按安全属性 | 安全共享内存 | 位于安全内存区域,仅安全世界可访问,用于敏感数据暂存 |
| 按数据方向 | 输入 / 输出 / 双向 | 决定 TEE 侧映射权限与参数校验策略 |
| 按生命周期 | 临时共享内存 | 单次调用内注册与释放,随命令结束回收 |
| 按生命周期 | 注册式共享内存 | 通过 register_shm 长期注册,多次调用复用,减少映射开销 |
| 按实现形态 | 连续 / 非连续 | 连续内存便于硬件加速器访问,非连续由页表拼接 |
三、与普通内存、寄存器传参的区别
很多初学者容易把共享内存与普通内存、寄存器传参混为一谈。三者在访问主体、容量、性能与安全语义上差异明显。
- 普通内存:进程私有,跨世界不可见。普通世界的 malloc 得到的地址在 TEE 侧没有任何意义,直接传指针会触发校验失败或访问异常。
- 寄存器传参:通过 SMC 调用的通用寄存器传递少量参数(如命令 ID、会话 ID、共享内存句柄)。容量极小,通常只有几个 32/64 位字,无法承载大块数据。
- 共享内存:容量可达 MB 级,需要显式注册与映射,访问前必须完成地址转换与权限校验,是“大参数”的唯一可行通道。
下面这段伪代码展示了典型的三段式传参:寄存器传句柄,共享内存传数据。
/* 普通世界侧:注册共享内存并获取句柄 */
TEEC_SharedMemory shm;
shm.size = 4096;
shm.flags = TEEC_MEM_INPUT | TEEC_MEM_OUTPUT;
TEEC_AllocateSharedMemory(&ctx, &shm);
/* 通过寄存器把句柄/偏移传给 TEE,数据本体在共享内存中 */
TEEC_Operation op = {0};
op.paramTypes = TEEC_PARAM_TYPES(
TEEC_VALUE_INPUT, /* 寄存器传小参数 */
TEEC_MEMREF_PARTIAL_INOUT, /* 共享内存传大块数据 */
TEEC_NONE, TEEC_NONE);
op.params[1].memref.parent = &shm;
op.params[1].memref.size = 4096;
TEEC_InvokeCommand(&ctx, CMD_PROCESS, &op, &err);
四、安全与非安全共享内存
安全属性是共享内存最关键的一条分界线。非安全共享内存位于普通 DRAM,普通世界可读写,TEE 侧映射时需经过 MMU 与 TZASC(TrustZone Address Space Controller)等硬件单元的区域检查,确保不会越界访问安全内存。安全共享内存则位于受保护区域,普通世界无法直接访问,只有安全世界能读写,常用于存放密钥、会话密钥、敏感中间结果。
在 OP-TEE 中,TEE 内核会对来自普通世界的共享内存参数执行严格校验:检查物理地址是否落在非安全区域、大小是否越界、方向是否与映射权限一致。若校验失败,调用会被拒绝并返回错误码,而不是放任访问。这正是共享内存机制区别于“裸指针传递”的安全价值所在。
五、典型场景与常见误区
以一个图像处理 TA 为例:普通世界把待处理图像放入非安全共享内存,通过寄存器传入句柄与长度;TA 在安全世界映射该缓冲区,读取图像、执行算法,再把结果写回同一块共享内存的输出区域。整个过程数据只搬运一次,避免了“复制到安全内存再复制回来”的双重开销。
常见误区包括:
- 把普通世界指针直接当参数传给 TA,误以为 TEE 能访问,结果触发地址校验失败。
- 忽略方向标志,把只读输入缓冲区当输出写,导致权限校验不通过。
- 共享内存未对齐或大小未按页对齐,映射时被拒绝。
- 认为共享内存天然安全,把密钥明文长期放在非安全共享内存中。
- 忘记释放或注销共享内存,造成内存泄漏与映射表膨胀。
综上,共享内存是 OP-TEE 跨世界通信的基石:它用“同一物理内存、双方各自映射”的方式解决了隔离环境下的数据交换难题,并通过方向标志、地址校验与安全属性区分,把“能传数据”与“安全地传数据”统一起来。理解它的定义、分类与传参差异,是后续进行性能优化与安全加固的前提。