Scalable Memory Protection in the PENGLAI Enclave
蓬莱是上交研究的RISC-V架构下的可信执行环境方案。
Introduction
目前已经有许多成熟的TEE方案,如Intel SGX,AMD SEV,ARM TrustZone,以及一些学术界的研究工作,但是过去的TEE方案都存在着一些缺陷,较明显的就是无法适应云端应用的特性:弹性的内存分配,高资源负载,auto-scaling(动态调整实例资源分配),短执行时间等。这往往是受限于方案设计的内存保护机制:
- 不可扩展的内存划分/隔离:大多数设计使用了硬件中单独为飞地存在的内存,具有硬件上的大小和数量限制
- 不可扩展的内存完整性保护:使用传统的Merkel Hash Tree方案校验完整性是难以扩展的,只能对固定大小的内存核验完整性
- 不可扩展的内存安全初始化:初始化一片飞地的内存往往需要数秒,但是服务本身的运行时间通常很短(<1s)
因此本篇文章提出的蓬莱设计就是要解决上述问题,在安全内存的大小和粒度、飞地的数量、启动飞地的延迟方面提供支持。
Motivation
目前的解决方案都存在着各自的缺陷,在可创建的飞地数量,可用的内存大小,启动飞地的速度,内存完整性保护的粒度,保护的额外开销,可信启动的证明能力等方面上表现出不同层级的限制。
蓬莱的目标就是解决上述已知的缺陷,提供可扩展的内存管理,支持不限量的飞地同时运行,以及可扩展的内存大小,在不添加过大开销的基础上,提供应对更高权限软件攻击,片外硬件攻击,基于缓存的侧信道攻击的安全支持。
System Overview
Architecture
蓬莱是一个软硬件协同的方案,软件实现了Secure Monitor用于管理并监视安全世界,硬件补充了下图中红色的新单元,以实现预期的功能目标。类似于TrustZone的想法,蓬莱也将系统状态划分为安全世界和不安全世界,分别运行不同的操作系统和软件,其中Secure Monitor运行于最高权限级。
Thread Model
蓬莱的可信基为CPU和Monitor软件正确性,Monitor通过安全启动的方式由ROM中加载,在加载完成后会通过RISC-V自身的内存安全机制(PMP)保护自身。
在蓬莱方案中,威胁模型有以下三个攻击面:
- 权限级软件攻击:攻击者可以完全地控制非安全世界的OS和软件
- 物理硬件攻击:攻击者可以截获并分析CPU与其他硬件(如内存)的通信信息
- 侧信道攻击:考虑控制信道攻击和基于缓存的侧信道攻击
这里补充一下侧信道攻击,以SGX技术为例,CPU通过指令切换状态,分别对应飞地内的程序和飞地外的世界,在这个过程中两个世界会共享许多硬件资源,这就导致了信息泄露的可行性。控制信道攻击一般指对页表信息的泄露利用,SGX方案中非安全世界试图访问安全世界内存时会发生Page Fault,因此可以以页为单位对安全世界进行探查,更进一步还可以利用页表的信息位,观察安全世界的逻辑行为信息。除此以外,流水线,分支预测器,缓存都可以通过侧信道攻击的方式泄露出安全世界的信息。
在本篇文章中不考虑其他的侧信道攻击,与本方案关注的内存安全无关,也不考虑Dos攻击。
Design
Fine-grained Flexible Memory Isolation
为了实现细粒度的内存隔离,蓬莱像许多方案一样维护了表明物理页表所属世界的bitmap,以页表为粒度进行内存隔离。传统的方案往往基于此,在进行虚拟地址转换时,进行两次查找,先查询bitmap确认所查页表是否属于安全世界,再进行二次读取,这导致了25.2%的额外开销。
而蓬莱方案则关注在页表的管理,观察到mapping的操作远小于访问内存的次数,蓬莱在硬件中添加了Guarded Page Table模块对页表进行管理,并尝试通过页表的管理避免二次读取,以降低额外的性能开销。
在Guarded
PT模块中,引入了两个新的寄存器reg_hptarea_start
和reg_hptarea_size
,HPT表示Host
Page
Table即不信任的主机的页表区域,通过这两个寄存器将主机可以使用的页表全部限制在一段连续的物理空间内。
当主机试图更改页表区域时,会触发trap交由secure monitor进行检查,通过secure page的bitmap对映射区域的属性进行检查,在非安全世界中只能映射到non-secure page,而在安全世界中则不受限制,可以对两个世界的内存进行操作。
回顾TEE的目标,我们需要禁止非安全世界访问安全世界,结合下图所示的MMU扩展,在虚拟地址转换的每一步过程中都将得到的下一级页表地址进行检查,是否处于hptarea
寄存器规定的范围中。
通过MMU,我们限定了非安全世界访问的页表范围只能处于hptarea
中,而在这一范围内页表的所有改动都将触发trap,由monitor进行bitmap核验后写入,因此限制了非安全世界对安全世界的访问。
因此,总结来看这一方案创新性地将安全检查的代价由访问内存时转移到了创建映射时,基于映射次数较少的观察,该方案可以显著地降低访问开销。
当然,这一方案还需要考虑TLB的存在,由于硬件中缺少直接作用于TLB的指令,TLB难以被直接修改,另外在切换到安全世界的过程中,TLB也会被刷新,因此不存在非安全世界直接通过TLB访问安全世界的可能。
总结来看,这一方案对硬件的修改较小(加入了bitmap和MMU的逻辑判断),唯一需求的物理连续区域是相对较少的页标区域,实现了页级别的内存隔离并减少了二次查找的访问开销。
Scalable Memory Integrity Protection
为了保护安全世界的内存完整性,过去方案采用了Merkle Tree的方式对连续内存进行哈希,形成树形结构并通过根节点的哈希值保护整棵树的完整性。这些方案普遍存在一些问题,如保护的是一段连续的特定地址内存,树节点存储代价很大,更新哈希值开销较大等。
蓬莱提出了硬件上的Mountable Merkle Tree对安全世界的内存进行完整性保护。MMT最基本的单位是SubTree,不同层级的Merkle Tree共同通过哈希对整片内存进行完整性保护,最下层的叶子节点对应4个1MB的页。根据每一级子节点数量的设计,MMT最多可以对512GB内存进行完整性验证。
为了防止Merkle
Tree中的数值不被篡改,蓬莱建立了依托于SoC的信任链,从可信的SoC出发,将树的根节点地址存于SoC中的Root-root
寄存器中,保证根不受篡改,依据根节点的哈希值递归保证子节点的哈希值不受篡改。
这里使用的哈希树是Counter-based Integrity
Tree,这里没有详细介绍,我看了Vault那篇文章才理解这个词的含义,具体的内容可以在Vault论文阅读摘要中查看。相比于传统的BMT,蓬莱方案中添加了两个域,idx
和extra
,这是为了减少对同一分支的修改多次触发global counter
域的更新,idx
指向刚刚更改的local counter
,而extra
则作为高位一同作为计数器,当extra
域也溢出或idx
更换时再更新global
域,以此减少路径哈希更新的频率。
上图为完整性信任链的建立,Root-root
存储在SoC中,认为是可信任的,所有子节点的地址信息和计数器信息都存储在一块称为MMT
meta-zone的区域中,完整性由root保护。这片区域的信息又保护了counter指向的Subtree
Node区域,存储了所需的数据信息,通过哈希值对Secure
Mem区域进行完整性的保护。
论文中提供了这样一幅图来表述Mountable的概念,Merkle Tree保护的是覆盖区域内所有内存的完整性,因此可挂载并不是指改变树的结构,而是可挂载到SoC上的意思。
所有对完整性的验证从某一叶子节点出发,到达某一个在SoC中存储(也就是挂载)的节点时停止。SoC中留有的硬件空间最多可以存储32个子节点信息,存满后就依据LRU进行换入换出,利用空间访问的一致性来减少数据更新的代价,在一次读写后可以访问更少数量的节点来校验完整性。相比于传统的MT,MMT不将一整条从根节点出发的链存储在SoC上,而是存储子树的低层级节点,提高了SoC空间的利用率。
在系统启动时,MMT meta-zone是一块可配置的地址空间,由bootROM进行初始配置并通过硬件访问权限设置进行保护。
考虑到安全世界和非安全世界存在着必要的交换,蓬莱还设置了hole
region的概念,这是一个可配置的连续区域,由寄存器reg_hole_va_start
和reg_hole_va_size
配置。在hole区域中,执行相反的完整性验证,即当reg_ms=ENCL_MODE
时不对hole区域做完整性校验,提供二者共享的内存。这需要在MMT中做出支持,不对代表hole区域的子树进行完整性校验。
Secure Memory Initialization with Shadow Fork
建立飞地的代价是必须要考虑的因素,为了减小初始化的开销,蓬莱借鉴了shadow-fork的思路。考虑在云服务场景,先建立一个具有必须的运行时环境的shadow-enclave,并进行哈希值证明,当建立飞地时,通过fork的思路,共享R和RX段,只拷贝W段,初始化栈。
初始化飞地比较大的代价其实是在验证阶段,需要对内存进行完整性的证明,而根据影子飞地,可以轻松地使用已经计算出的证明,证明启动的飞地是可行的,再基于此进行进一步的保护即可。
On-demand Cache Line Locking
蓬莱设计也考虑了针对缓存的侧信道攻击,提供了CACHE_LINE_LOCK
和CACHE_LINE_UNLOCK
原语,对cache
line硬件层上锁,使得内存读写时间一致。这不可避免地会降低性能,因此蓬莱建议只在加密等较高安全需求的部分使用。在不同的飞地间切换时,cache的锁会作为上下文随之切换。
Implementation
Hardware Implementation
蓬莱把新增加的寄存器做成了CSR,扩展了MMU的Page Table Walker,增加了数据的合法性验证和HPT核验。在核外,蓬莱扩展了Memory Controller以实现MMT功能,增加了Mount Table和Root-root,Mount Table结构与Cache类似,同样使用LRU策略进行换入换出。
Software Implementation
蓬莱在OpenSBI和Berkeley Boot Loader上都做了Secure Monitor的实现,它包含了飞地管理,硬件扩展部分管理,内存检查,以及一些基础的库支持,仅需要6399行代码。
蓬莱同时也对Linux Kernel做了改动,主要是为了适配HPT的设计,分配HPT区域,劫持对于页表的修改行为,转交monitor进行判断。
飞地的类型包括普通飞地(用以创建可信执行环境),影子飞地(用以快速创建飞地),服务飞地。服务飞地并不能单独存在,需要从其他飞地继承时间片,用以构建飞地链,服务飞地可以提供部分OS的服务,如文件系统等。
蓬莱的形式化验证工作正在进行中。
Evaluation
蓬莱在VC707开发板上基于SiFive Freedom U500实现,采用了SPECCPU,Redis,RV8,Coremark测试集,与Linux(作为基准),Keystone进行整体的性能对比。也在GEM5上进行模拟实验,比较MMT和SIT,VAULT的完整性保护方案代价。
通过实验结果得到的结论整理如下:
- GPT会在mapping阶段产生26%~46%的额外开销,在内存访问阶段基本不会产生影响
- Shadow-fork可以大量节省启动飞地的时间开销
- MMT在随机内存访问测试中表现出色,在较大访问范围测试中由于mount机制,不会产生过大的开销。在随机访问测试中,mount操作只占用了10%的总访问时间,而完整性验证占了20%。
- Cache Locking会产生17.73%的额外开销
- 对硬件的额外改动基本不需要占用硬件资源
Discussion
实现蓬莱方案,需要两点支持:指令集需要支持比OS和hypervisor更高层级的特权级支持(现代指令集都支持),CPU需要有MMU模块以支持不同粒度内存管理(现代CPU中基本也存在)。
针对内存管理的侧信道攻击讨论:
- OS无法直接操作页表,避免了对页表观测的攻击
- Cache可以选择性地加锁,避免了对缓存时间的攻击