近年来,人们对微核进行了广泛的研究几十年了。但是,进程间通信仍然是运行时开销的主要因素,细粒度隔离通常会导致过多的IPC。ipc的主要开销来自于内核的参与,包括模式切换和地址空间更改的直接成本,以及由于处理器结构的污染而产生的间接成本。
本文介绍了一种为微内核同步IPC而设计和优化的新型通信设备SkyBridge。SkyBridge在通信过程中不需要kernels的参与,它允许进程直接切换到目标进程的虚拟地址空间并调用目标函数。SkyBridge保留了传统的虚拟地址空间隔离,因此可以很容易地集成到现有的微内核中。SkyBridge的关键思想是利用一个用于虚拟化的商品硬件特性(即VMFUNC)来实现高效的IPC。
为了利用硬件特性,SkyBridge在原始微内核(Subker)下面插入了一个很小的虚拟化层(Rootkernel)。Rootkernel经过精心设计以消除大多数虚拟化开销。SkyBridge还集成了一系列技术来保证IPC的安全性。
我们已经在三种流行的开源微内核(seL4)评价结果表明,天桥技术使微基准的IPC速度提高了1.49倍至19.6倍。对于实际应用程序(例如SQLite3数据库),Skybridge平均将三个微内核的吞吐量提高了81.9%、1.44倍和9.59倍。
在过去的四十年里,人们对微核进行了广泛的研究。关键的设计是剥夺大多数内核功能到不同的服务器驻留在孤立的用户进程。kernel提供了一些基本功能,如进程管理、能力实施和进程间通信(IPC)。这种分散的设计使得操作系统架构对运行时错误非常健壮,这意味着一台服务器中的错误不会影响其他服务器和系统。
从内核中删除大部分功能也会导致一个小型的可信计算基础(TCB),使其不易受到攻击,并有可能进行全面的正规验证[36]。鉴于这些优势,microkernels[29,34]被广泛应用于航空航天、汽车和医疗等涉及高再保险责任的各个领域。
在微内核中,不同进程之间的任何通信都是基于IPC的,IPC是一种广泛使用的操作。例如,如果客户机进程将数据写入外部块设备,它首先与文件系统通信,文件系统又通知磁盘设备驱动程序将数据写入块设备。所有的通讯都是通过IPC。
事实上,IPC被认为是运行时开销的一个主要因素[20,27,40,52],它决定了应用程序在微内核上的性能。跨越进程边界传输控制是昂贵的,这至少需要:陷入微内核(SYSCALL指令)、参数的数据复制、一个地址空间转换(如果考虑到最近的熔毁攻击,则为两次转换[45])和一个回到用户态。此类操作必须在IPC返回后重新进行。一些IPC的异步实现甚至涉及到昂贵的调度工作。
大量的研究已经完成,以优化ipc性能。基于软件的解决方案试图通过删除不必要的操作来缩短IPC路径。seL4[36]在IPC消息适合CPU寄存器的情况下,使用IPC 快速路径进行呼叫和应答系统调用,没有能力转移。对于快速路径,消息将立即发送,控制流将直接传输,而无需进入昂贵的调度逻辑。类似地,一些基于软件的解决方案(如LRPC[8])也消除了调度开销,并允许进程的线程在接收器的地址空间中执行请求的程序。然而,所有这些方法仍然需要内核的参与,因此它们的性能(IPC往返约1000个循环)不符合IPC密集工作负载的要求,如第2节所示。
基于硬件的解决方案提出了新的硬件扩展来促进IPC的运行。dIPC[55]将所有IPC参与者放入一个地址空间,然后从IPC路径中删除kernel。新设计的标记存储器实现了进程隔离。这种基于硬件的解决方案通常需要对硬件和软件进行非常重要的修改,实际采用的可能性较小。
因此,我们认为需要满足以下要求的IPC技术。
高效:IPC路径不涉及内核
轻量级:IPC可以很容易地部署在兼容的硬件上,并且可以很容易地集成到现有的微内核架构中。
安全:IPC设计不会破坏微内核隔离抽象
本文提出了一种新的IPC设计方案。我们的设计称为SkyBridge,它允许一个进程(发送方)在另一个进程(接收方)的地址空间中直接执行请求的过程,而无需陷入内核。天桥有两个主要的技术优势。首先,SkyBridge仍然将每个进程放在自己的虚拟地址空间中,这与现有微内核的设计和实现非常吻合。第二,Sky Bridge利用Intel的一个虚拟化硬件功能EPT(扩展页表)交换(VM-FUNC指令),在用户级别更改虚拟地址空间。通过配置接收方的EPT,SkyBridge将发送方的页表映射到接收方的页表。
因此,在通过VMFUNC切换EPT之后,硬件使用接收方的页表来翻译所有随后的虚拟地址。SkyBridge还为每个接收方的虚拟地址空间中的线程提供了一个单独的堆栈。
为了支持长IPC,SkyBridge在传输大消息时为IPC参与者提供共享缓冲区。每个缓冲区都绑定到一个接收方的线程以实现并发。尽管天桥的方法听起来很直观,但将其应用于微内核会带来三个实际挑战。首先,利用EPT交换需要虚拟化层,这可能会给整个系统带来开销,因为新的层可能会导致大量的costly VM exits.为了应对这个挑战,SkyBridge 引入了一个小型的虚拟化层叫做Rootkernel,只包含最原始的天桥功能,同时在IPC期间消除VM出口。
第二,现有的方法利用VMFUNC[41,46]需要对微内核进行不小的修改,从而需要大量的工程工作。SkyBridge提出了一种轻量级的方法,可以有效地在不同的进程之间切换虚拟地址空间,这种方法可以很容易地集成到微内核架构中
第三,在没有内核参与的情况下,很难设计安全的IPC设施,特别是当一个恶意进程可以利用VMFUNC指令破坏其他进程时[46]。SkyBridge保证只有一个合法的入口点用于在进程之间切换地址空间,这可以防止恶意进程调用自行准备的VMFUNC指令来损坏其他进程。SkyBridge还需要一个进程在与其他进程通信之前注册到其他进程,并引入了调用密钥表机制来实施这样的策略。
贡献:
在本节中,我们将评估与微内核中传统的同步进程间调用(IPC)相关的性能开销。我们在英特尔Skylake处理器上使用seL4(v10.0.0)来进行所有的实验。众所周知,seL4有一个快速的IPC设备,我们相信它可以代表最先进的微内核。
虽然集成了不同的优化技术,但是目前同步IPC的实现仍然会对通常是IPC密集型的微核工作负载的性能产生负面影响。同步ipc开销可分为两类:1.内核的直接成本 2.处理器结构的间接污染。
模式转换:对于每个IPC,发送方首先调用系统调用陷入到内核中,然后将必要的用户模式状态保存到内核堆栈中,当内核恢复接收者执行时,他恢复接收者的用户模式状态,然后最终调用sysret指令回到用户模式。IPC中的模式切换还包含两条SWAPGS指令,它们在进入和退出内核时更改gs寄存器的基址。我们通过执行一个直接返回到用户模式十亿次的null系统调用来度量模式切换成本。为了测量每个操作的开销,我们在每个指令之前和之后读取时间戳计数器(TSC)值。SYSCALL、SWAPGS和SYSRET的周期分别为82、26和75。
地址空间切换:微内核使用不同的地址空间来隔离进程。因此在交付ipc时有必要切换虚拟地址空间。在启用PCID(进程ID)的情况下,我们机器上的地址空间切换的测量成本是186个周期。此外,微内核和用户空间用不同的页表来抵御melt-down攻击,因此一个ipc通常包含两个地址空间转换,转换需要372个周期。
其他软件ipc逻辑:为了处理IPC请求,微内核通常包含各种安全检查、端点管理和功能强制。这部分的总成本是98个周期的seL4快速路径在我们的机器上。
同步IPC的开销并不局限于内核的直接开销。在内核的执行过程中,它会逐出一些重要进程或结构中的用户模式状态,包括一级指令和数据缓存、二级和三级共享缓存以及转换暂放缓冲区(translation look aside buffers,TLB)。状态污染会间接影响后续的用户模式指令,从而触发TLB未命中和不同级别的缓存未命中。
为了评估处理器结构的间接污染所造成的开销,我们在seL4微内核中构建了一个简单的键值存储。它由一个客户机和两个服务器组成,这两个服务器是一个加密服务器和一个密钥值(KV)存储服务器,如图1所示。对于插入操作,来自客户端的请求在到达KV存储服务器以保存消息之前到达加密服务器以加密消息。对于查询操作,加密服务器解密来自KV存储服务器的查询结果,然后将其返回给客户端。
有三种方法来组织这三个过程:
基线:将它们放在同一个虚拟地址空间中,并利用函数调用将它们连接起来。
ipc:将它们放入不同的虚拟地址空间,利用IPC将它们连接起来。seL4内核的配置没有使用熔毁缓解。
延期:将它们放入同一个虚拟地址空间,并利用延迟函数调用来连接它们。延迟函数调用使用一个循环来延迟一段时间,这相当于一个IPC的直接成本(493个周期)。
我们测量了键值和价值大小对基准吞吐量的影响。客户端的请求包括50%/50%的插入和查询操作。理想情况下,IPC和延迟条之间应该没有区别。如图2所示,IPC的间接成本是IPC与延迟条之间差距的原因。
我们还利用Intel Performance monitoring unit(PMU)统计了三个实验中512次操作发生的不同事件。表1显示了几种处理器结构的封装外形。数据表明IPC版本对缓存和TLB结构的影响比延迟和基线情况下更为显著。
在多核机器中,服务器和客户机可能在不同的核上进行核处理,这是由于过度订阅条件或调度决策造成的。在这种情况下,跨核IPC需要代价高昂的处理器间中断(IPI)。例如,跨核IPC退化为包含IPI的慢路径版本。在我们的机器上,一个IPI需要1913个周期。我们通过将客户机及其两台服务器放在三个不同的核心上,重新评估IPC 版本实验。结果也如图2所示。对于不同长度的键和值,跨核IPC会产生很大的开销。
VMFUNC是一条英特尔硬件指令,允许非根模式(内核和用户模式)下的软件调用VM函数。VM函数是由hypervisor管理的处理器特性。EPTP(指向EPT的指针)切换是这些VM功能之一,它允许客户机从存储在hypervisor配置的虚拟机控制结构(vmc)中的EPTP列表中加载EPTP的新值。然后,新的EPT将子序列来宾物理地址(GPA)转换为主机物理地址(HPA),EPTP列表最多可以容纳512EPTP条目。EPTP交换的典型用法是为一个物理地址空间创建多个域,这些域通常具有不同的内存映射和权限[30,46]。表2显示了不同指令和操作的延迟。启用虚拟处理器ID(VPID)功能后,VMFUNC指令不会刷新TLB,只需134个周期。
传统的IPC实现需要内核的参与,这会带来我们在第二节中分析的直接和间接代价。因此,为了解决传统同步IPC对性能的影响,Sky-Bridge旨在消除同步IPC中的内核参与。它允许客户端直接切换到服务器的虚拟地址空间并执行请求的过程,这不仅避免了陷入微内核的直接成本,而且部分消除了体系结构状态污染的间接成本。
SkyBridge使用一个VMFUNC来实现虚拟地址空间的切换,而不必陷入内核,并消除代价高昂的IPI,因为它允许一个进程直接调用另一个进程的代码。Skybridge的一般工作流程和示例代码如图3和图4所示。SkyBridge提供了一个类似于传统IPC的编程模型。要使用SkyBridge,应修改程序以使用SkyBridge用户级接口。
在其他进程开始请求其服务之前,服务器必须先注册到内核中。在注册过程中,服务器为内核提供一个函数地址,它允许其他进程直接调用该地址,并提供一个数字,表示它可以同时接收的最大连接数。然后内核将与trampoline相关的代码和数据(共享页和堆栈)映射到服务器的虚拟地址空间,并返回服务器ID,客户端可以在注册期间使用该ID来定位服务器。类似地,客户机还通过提供它打算调用的serverID注册到SkyBridge中。然后内核将与蹦床相关的代码和数据页映射到客户机的虚拟地址空间。它还将服务器函数列表映射到客户机虚拟地址空间。最重要的是,内核为客户机和所有目标服务器创建一个EPT,如图3中的步骤1所示。在这些服务器的ept中,客户端的页表映射到相应的服务器页表。
当客户机调用direct_server_调用(图3中的步骤2)时,trampoline将客户机的状态保存到其堆栈中,并调用VMFUNC指令切换到服务器的EPT。在切换EPT之后,服务器EPT中的配置确保硬件使用服务器的pagetable来转换所有后续的虚拟地址。最后,trampoline安装服务器的堆栈并调用服务器的注册处理函数。
但是,应用VMFUNC会带来以下挑战。
虚拟化开销。使用VMFUNC需要在微内核的底部插入一个hypervisor,这不可避免地会导致系统正常执行的开销。
整合。现有的利用VM-FUNC的技术很难在不做太多修改的情况下应用于微内核。
安全。通过直接虚拟地址空间交换机的设计,提出了新的安全威胁。
为了利用天桥,天桥提出了一系列解决方案来应对这些挑战。
高效的虚拟化:虚拟化的开销主要来自两个方面。第一种是两级地址转换,另一种是大量的有代价的VM出口。为了解决虚拟化开销,SkyBridge引入了一个只包含1500个LOC的小型虚拟机监控程序(Rootkernel)。它利用1GB的大页面将除了为Rootkernel保留的内存以外的大部分主机物理内存映射到非根模式下的微内核(本文称为子内核)。这种内存映射不仅允许微内核的执行不会触发任何EPT冲突,而且还降低了从GPA到HPA的地址转换成本。为了解决第二个开销,rootkernel将VMCS配置为让子内核处理大多数硬件事件(外部中断)或特权指令。通过使用这样的配置,大多数VM出口都是无效的。在我们的评估中,运行正常应用程序时没有虚拟机出口,虚拟化开销是不可忽略的。
因此,SkyBridge的架构被分为两个组件,如图5所示。根内核由三部分组成,即EPT的管理、不可避免VM退出的HANDLERS和一个自虚拟化模块。子内核有一行代码来调用Rootkernel中的自虚拟化模块,以便在引导过程中动态启动Rootkernel。进程创建部分也被修改为调用Rootkernel的EPT管理部分来为每个新进程配置EPT部分。在创建新进程时,子内核将trampoline映射到进程中,这有助于进程调用SkyBridge的直接服务器调用。根内核和子内核的详细信息在第4.1节和第4.2节中描述。
轻型的虚拟地址空间切换:为了在不涉及内核的情况下使用VMFUNC在用户模式下有效地切换虚拟地址空间,有一种可能的设计,它是将所有相关的进程合并到同一个虚拟地址中,并使用不同的ept来隔离他们。交换虚拟地址空间意味着安装一个启用相应权限的新的EPT。即使解决方案听起来很直观,它也需要对微内核进行非琐碎的修改,以解决可能的虚拟地址重叠问题,从而招致巨大的工程努力。SkyBridge提出了一种轻量级和有效的设计,在每个EPT中重新映射页表的基址(CR3值)。SkyBridge没有将所有进程放在同一个虚拟地址空间中,而是使用不同的页表将它们隔离开来。在调度新客户机之前,skybridge将为其安装一个新的EPTP列表,其中包含允许客户机调用的服务器的EPT指针,在每个服务器的EPT中,客户机的CR3值的GPA被转换为相应服务器的CR3值的HPA,这允许硬件在调用VMFUNC指令后自动使用新的页表进行后续的虚拟地址转换(第4.3节)。
安全蹦床:在传统的微内核设计中,每一个IPC都陷入内核中,由内核进行安全检查,拒绝任何非法的IPC通信。然而,内核无法检查SkyBridge中的每个IPC通信,这意味着SkyBridge必须提供新的技术来保证与传统IPC相同的安全级别。首先,恶意进程可以使用自己准备的VMFUNC指令绕过蹦床,访问其他进程的敏感数据或代码,在[46]SeCage中称为VMFUNC伪造攻击。然而,SeCage提出的防御方案在Sky Bridge中失败了(我们将在第4.4节中解释)。为了防止这种攻击,SkyBridge动态重写进程的二进制文件,用功能等价的指令替换任何非法的VMFUNC。为了防止发送方调用未注册的接收方,我们为每个进程提供了一个调用键表,该表记录了调用键的列表。
对于每个IPC,发送方应该为接收方提供一个调用密钥,接收方根据其调用密钥表检查该密钥,并拒绝IPC,如果发送的密钥不在表中,则通知内核。这个解决方案提供了一个乐观的安全检查,它假定大多数IPC是合法的,不需要内核对其进行检查(章节4.4)。
要使用VMFUNC,进程必须在非根模式下运行,但是,是否将内核置于非根模式有两种设计选择。一种方式,如sescage[46]和CrossOver[41]的工作方式类似于虚拟机,其中内核和进程都以非根模式运行。通常,这些系统重用成熟的商业管理程序,如KVM[35]和Xen[6],它们被设计为支持通用虚拟机,因此,这种方式将导致虚拟化层带来的巨大开销。另一种设计选择是将内核置于根模式,同时以非根模式维持进程,如Dune[7]。然而,代价高昂的VM出口仍然存在。在Dune中,大多数系统调用都是VM退出的成本,这比(非虚拟化的)系统调用要昂贵得多。
天桥提供了不同于上述两种设计选择的新解决方案。它消除了以前的解决方案导致的昂贵的VM退出。SkyBridge提供了一个名为Rootkernel的小型虚拟机监控程序,其大小比商业虚拟机监控程序小得多。Rootkernel只包含支持SkyBridge的必要功能,包括EPT管理、动态自虚拟化模块和一些基本的VM出口处理程序。
为了消除昂贵的VM退出,根内核配置硬件,让大多数VM行为不会触发任何VM退出。VM退出包括特权指令退出、硬件事件退出和EPT违反退出三种类型。对于特权指令退出,比如更改CR3值,Rootkernel允许这些指令不触发VM退出。为了处理硬件事件,比如传统管理程序中的外部中断,VM exit会在接收到这个事件时触发来唤醒管理程序。在SkyBridge中,Rootkernel允许硬件以非根模式将外部中断直接注入微内核,因为它有权管理其外部设备。
商业hypervisor为每个VM使用一个EPT,并在EPT中指定属于虚拟机的内存区域。这个可以限制此虚拟机访问其他虚拟机和监控程序的物理内存。当虚拟机访问EPT中不存在的物理内存或没有足够的权限访问该物理内存时,一个EPT违规VM exits将触发,hypervisor将唤醒来处理此类违规。此外,2级地址转换(从GVA-HPA)比1级地址转换(从GVA到GPA)产生更高的开销。例如,2级地址转换中的一个TLB未命中可能最多需要24个内存访问[26],这会产生很大的开销。为了消除EPT冲突VM exits并减少2级地址转换的开销,Rootkernel为子内核创建一个基本EPT,并使用最大的巨大页面(在x86_64机器上为1GB)将大多数物理内存地址映射到子内核。SkyBridge只为rootkernel保留了一小部分物理内存(在我们的评估中是100MB)。因此,微内核可以自由访问机器上几乎所有的物理内存,并且不会再触发EPT冲突。此外,巨大的页映射不仅减少了处理TLBmiss的内存访问次数,而且还减少了TLB未命中的次数。
Rootkernel的引导过程不同于传统的hypervisor。受CloudVisor[61]的启发,Sky Bridge不包含增加Rootkernel复杂性的机器引导代码,这是错误的-倾向。相反,根内核由子内核引导,并将子内核降级为非根模式。在不可避免的退出时,根内核也保留处理程序。在我们当前的实现中,根内核包含CPUID指令、VMCALL指令和EPT违反的处理程序。VMCALL指令被根内核用来实现一个与子内核通信的接口。
Rootkernel为子内核提供了一个接口来管理每个进程的EPT。当服务器注册到SkyBridge时,子内核将trampoline代码页和许多堆栈页映射到服务器的虚拟地址空间。然后为服务器分配一个空闲的服务器ID。当客户机注册到SkyBridge并请求进入服务器时,Rootkernel将蹦床代码和堆栈页映射到客户机的虚拟地址空间。然后子内核调用Rootkernel接口,请求Rootkernel在EPT级别绑定客户机和服务器。rootkernel复制一个新的服务器EPT并将client页表映射到server 页表。最后,rootkernel将新创建的EPT安装到客户机的EPTP列表中。实际上,Rootkernel还将服务器依赖的所有进程的EPTP写入客户机的EPTP列表。
SkyBridge不修改微内核的调度算法部分。然而,当Subkernel决定从一个进程到一个新进程进行上下文切换时,它会通知Rootkernel安装下一个进程的EPTP列表。天桥为传输大数据的进程提供共享缓冲区。子内核根据为一台服务器注册的线程数创建多个共享缓冲区。每次客户机调用服务器时,它都可能使用server thread的缓冲区进行大数据传输。
过程误认警告:如果发送方在接收方的虚拟地址空间中执行时陷入在内核中(例如,由中断引起),它打算作为接收方调用微内核的服务。然而,微内核仍然会将进程视为原始进程,我们称之为进程错误识别问题。为了解决这个问题,SkyBridge分配了一个标识页来记录每个进程的标识信息,并将该页映射到每个EPT中的相同GPA中。这个页面也被映射到内核地址空间,因此Subkernel可以通过一个虚拟地址访问这个空间。Subkernel通过访问这个虚拟地址来检查这个页面,以知道它正在为哪个进程服务。
SkyBridge保证了不同进程的虚拟地址空间是隔离的,并为它们提供了一种高效的用户级虚拟空间切换方法。事实上,有两种已知的技术可以达到这种目的。第一项技术[46]是将不同的进程放入同一虚拟地址空间,并为每个进程使用一个EPT来提供共享虚拟地址空间的独立视图。与天桥类似,它还使用VMFUNC在不涉及内核的情况下切换视图。当进程数较少时,这种技术很容易实现。当数量变大时,必须仔细管理不同进程的虚拟地址区域,以防止这些区域重叠,这需要繁琐的工程工作。
第二种技术是利用英特尔最新的用户空间内存保护密钥(PKU)来切换视图。应用PKU也不能解决重叠问题。此外,它提供的安全域(16)数量有限,不能满足微内核的要求。
天桥是对传统虚拟内存隔离方法的继承,提出了一种高效的虚拟空间切换技术,不需要大量的工程工作来实现。与第一种技术不同的是,SkyBridge仍然为这些进程使用分离的页表。要在usermode中切换页表而不修改CR3寄存器,它将客户机的表页基址(CR3值)重新映射到服务器EPT中服务器CR3值的HPA。因此,通过调用VMFUNC切换EPT可以暂停服务器的虚拟页表,以便以后进行虚拟地址转换。
SkyBridge用于提供高效页表切换的技术如图6所示。进程仍然由微内核的原始机制创建并拥有其虚拟地址空间。在本例中,客户机和服务器有它们自己的页表,其基本物理地址(GPA)分别是client- cr3和server- cr3。一旦启动一个新服务器,子内核保存服务器的CR3值(server-CR3)。当客户机注册时,子内核通知Rootkernel为客户机和服务器从基本EPT复制两个新的EPTs,它们分别是EPT-C和EPT-S。然后Rootkernel将client-CR3重新映射到EPT-S中server-CR3的HPA,并且不对EPT-C进行任何修改。
在执行过程中,CR3寄存器的值为client-CR3,不会更改。当客户端调用direct_server_call接口时,trampoline调用VM-FUNC指令将EPT指针的值从EPT-C更改为EPT-S。在使用EPT-S之后,client-CR3将被映射到server-CR3的HPA,这意味着所有后续的虚拟地址都将被服务器pagetable转换。因此,客户机可以访问服务器虚拟地址空间中的任何虚拟地址。
请注意,这里创建的EPT只是一个浅层复制,它重用了基础EPT中的大多数映射。仅修改了将client-CR3映射到server-CR3的HPA的四个页面。所有其他EPT页面保持完整。
SkyBridge的直接服务器调用接口由蹦床实现,蹦床是进程注册时子内核映射到虚拟空间的代码页,客户端及其所有绑定服务器应插入suchtrampoline。将客户机绑定到服务器时,子进程会创建多个堆栈,并将它们映射到服务器的虚拟地址空间。服务器在注册期间指定堆栈的数量,并确定服务器可以支持的manyconcurrent线程的数量。
通常,发送者需要在IPC中将一些数据传输到接收端。对于小型数据传输,SkyBridge会将这些数据放入CPU寄存器中,CPU寄存器遵循x86\U 64中的调用约定。对于大数据传输,SkyBridge为每对客户端和服务器线程创建SA共享缓冲区,并将它们映射到客户端和服务器中。
蹦床Workflow:When the 发送方是一个客户机,它的IDis为零。否则,发送方ID是register\u server函数返回的值。在调用vmfuncuit之前,如果传输的数据超过CPU寄存器的容量,蹦床将客户机内部缓冲区中的数据复制到共享缓冲区。通过调用VMFUNC指令来切换虚拟地址空间后,蹦床将安装服务器堆栈。最后,它根据服务器ID调用服务器的注册函数。
安全Threats:The design 在蹦床上考虑了两种可能的攻击。第一种攻击是自行准备的VM-FUNC攻击,恶意客户端或服务器调用trampoline未准备的非法VMFUNC指令以绕过trampoline并访问其他进程中的敏感指令或数据。以前针对这种攻击的防御措施是将不同的applicationlogic(PAL)代码和数据放入不同的ept中,并确保只有蹦床映射到这些ept中,这使得它成为其他PAL的唯一入口点。但是,由于CR3 GPA技术的重新映射,此解决方案不适用于SkyBridge,这允许攻击者在任何虚拟地址调用EVMFUNC以切换到受害者进程的虚拟地址空间。因此,蹦床并不是其他过程的唯一合法入口。
为了防御这种攻击,我们利用二进制重写技术,扫描每个进程的代码,将任何VMFUNC指令或包含意外VMFUNC的指令序列与其他功能等效的指令重新放置。此解决方案已被其他不同的系统使用[18、50、53],我们将在第5节中描述我们的方法。
第二次攻击称为非法服务器调用或client return。非法的服务器调用是一个客户端可能绕过它应该调用的服务器,并直接调用未注册的服务器,如果这些服务器包含敏感信息,这是危险的。类似地,非法客户端返回的是,oneserver不会返回调用它的客户端,而是返回到其他客户端或服务器。为了抵御这种攻击,SkyBridge为每个进程提供一个调用密钥表,该表记录绑定到它的进程及其调用密钥。服务器的缩放密钥是在客户端期间生成的registration.TheSubkernel 为客户端生成随机8字节密钥,并将其提供给客户端和服务器。例如,client获取服务器的调用密钥并将其传递给服务器,在服务器上,当它没有找到给定的key inits表时,它会检查其表中的调用密钥,而没有tifice子内核。
每次客户机调用其服务器时,它还动态生成一个客户机调用密钥并将其传递给receiver.The 接收方应将此密钥返回给发送方,并将其记录下来,以确保接收方之前调用的是该密钥。
恶意进程可能会故意将其密钥泄漏给其他进程。但是泄露的密钥只暴露属于密钥所有者的敏感信息,不会暴露其他数据,因为服务器可以使用调用密钥来识别调用进程
当一个进程注册到SkyBridge时,子内核会扫描该进程的所有代码页,以用功能等效的指令替换trampoline代码页之外的任何非法VM-FUNC指令。重写后,一条指令可能会被更改为两条或多条指令,这些指令不能保存在原来的位置。因此,SkyBridge用跳转指令替换这些指令,跳转指令跳转到页面进行重写。重写页由子内核插入,并映射到一个未使用的虚拟地址。我们选择虚拟地址空间中的第二页(从0x1000开始)。对于大多数操作系统,此页故意保留为未显示。在插入的页面中,子对象为新指令创建一个代码段。在每个代码段的末尾,子内核还附加一条新的jumpinstruction以返回到原始代码页。
重写策略受ERIM[53]的启发,ERIM使用了类似的策略来替换WRPKRU指令。该策略是完整的:任何无意中的VMFUNC指令都可以重写为功能等效的指令。该策略高度依赖于x86可变长度的指令编码。英特尔x86指令编码由五个区域组成:1)可能带有前缀的操作码字段(VMFUNC的操作码包含三个字节:0x0F、0x01、0xD4);2) 可选的1字节Mod R/M字段描述了该指令的寻址模式和两个操作数;3) 可选的1字节SIB(第二寻址字节)字段,指定间接存储器寻址;4) 一个可选的位移,作为Mod R/M字段描述的寻址模式中使用的偏移量;5) 指令的可选立即数字段。
VMFUNC指令有三种解码条件:•C1:该指令确实是VMFUNC。•C2:通过跨越两条或多条指令进行编码的VMFUNC。•C3:长指令包含VMFUNC编码。要对这三种条件进行分类,子内核将在扫描过程中预订保持当前指令,这有助于确定指令的边界。对于C1,子内核只是用三条NOP指令(0x90)替换非法的VMFUNC。对于C2,跨越两个或多个指令的非法VMFUNC可以通过在这些连续指令之间插入NOP来“破坏”。
当VMFUNC存在于一条较长的指令(C3)中时,子内核将用其他功能等效的指令替换该指令。表3列出了所有可能的情况及其相应的重写策略。字节0x0F是操作码的isanescape前缀,不会出现在任何指令操作码的中间字节中。因此,如果一条指令的操作码的第一个字节与0x0F重叠,那么这个指令就是VMFUNC,它的重写策略已经被忽略了(用三条NOP指令代替).
如果0x0F等于Mod R/M字段(即1字节),它就确定rcx(r9andecx)和rdi(r15andedi)是指令的操作数。子内核将其中一个寄存器(例如rdi)替换为另一个寄存器,该寄存器的值将被提前推入堆栈。例如,Subker nel将srdiwithraxin替换为表3。如果0x0F等于SIBfield(也就是1字节),则此指令还使用fixedregister,并且SkyBridge应用类似的重写策略来替换它。
当0x0F与位移重叠时,保留的两个字节(0x01和0xD4)可能适合位移或立即数字段。如果三个字节都位于位移字段中,SkyBridge会在指令之前预计算位移值(例如第4行)。如果三个字节中的某个字节与立即数字段重叠,Sky Bridge将两次应用该指令以获得相同的效果。对于类跳转指令,立即数将被视为偏移,当我们在重写页重写此指令时,该偏移将被更改为新值。