专栏首页Rust语言学习交流【投稿】在Intel SGX环境下实现Rust原生std支持

【投稿】在Intel SGX环境下实现Rust原生std支持

简介:Intel SGX是一个把应用与OS完全隔离的可信执行环境,应用无法直接访问OS提供的资源。我们采用的Teaclave-SGX-SDK只提供了no_std环境,导致crates生态下大量的库都无法被使用。我们通过添加libc函数模拟linux平台特性,实现依赖std的Rust生态库无需修改即可在SGX环境使用。为了保证尽可能小的安全边界,我们对每个增补的libc函数做了权限控制。同时引入了二进制分析,确保程序不会出现SGX非法指令。


背景

Phala Network的隐私云计算服务基于teaclave-sgx-sdk开发,由于Intel CPU的SGX执行环境相当于裸机无系统,自然地基于teaclave-sgx-sdk开发的rust程序也只能用no_std开发。

但当项目复杂后,我们还是希望能够充分利用Rust的crate生态,这个生态里大部分crate是依赖rust std的。我们no_std环境想要使用这些std的crate的话那就得做移植了。

如果单纯地将std的crate移植到no_std环境,那每一个crate都会有比较大的工作量。teaclave-sgx-sdk为了方便移植,给我们准备了一个sgx_tstd(一个sgx环境的std仿制品)。sgx_tstd保留了rust std中的大部分功能,因此,一般简单的crate移植到sgx_tstd仅需要改动数行代码,比如在crate根部添加extern sgx_tstd as std,以及添加一些use std::prelude::v1::*; 。这样移植rust crate生态就方便了许多,teaclave-sgx-sdk团队甚至将一些常用的crate都移植好了放到github.com/mesalock-linux中.

sgx_tstd的问题

移植一个crate看上去工作量并不大,但我们很多时候引入一个crate并不是单纯的一个crate,他背后的依赖树连根拔起可能有数十个crate。原本正常Rust生态使用一个第三方crate只需要在Cargo.toml中添加一行代码,而现在变成要去移植一大车crate到sgx环境。

这种开发模式已经事实上导致了Rust生态被分叉成了crates.iomesalock-linux两个互不兼容世界。这种分裂甚至让一些no_std的crate也受影响,比如混用一些依赖log或serde的no_std crate就不能正常编译,不得不修改他们使用log-sgx和serde-sgx。如果哪天有人再为arm/AMD的TEE做一个类似的rust sdk,难道我们要将crates.io继续分叉下去?

这种分叉行为同时会导致被移植的生态可能代码更新不及时,一些针对crates.iogithub.com的安全扫描公共设施可能也会漏掉mesalock-linux生态中的隐患,从而影响下游开发或带来安全威胁。

让SGX支持Rust原生std

teaclave-sgx-sdk开发应用目前标准做法是开启#![no_std]并编译target到 x86_64-unknown-linux-gnu

既然已经target到了x86_64-unknown-linux-gnu,那么我们如果不开启no_std编译会有什么问题呢?

简单尝试会得到类似如下链接错误:

Rust的std会依赖libc来和OS交互,intel sgx-sdk里面有一个不完全实现的sgx libc。但Rust需要的和系统交互这部分libc函数往往是SGX不信任的,所以sgx libc没有直接提供,而是大部分实现在ocall模块下以rust ABI函数的方式提供对等功能,以此提醒开发者这是不受信任的操作。

因此,我们想提供一个转接层,把这些缺失libc函数都补齐,并代理到sgx-sdk的对等实现基本就能正常编译使用原生std了。比如,上图缺失write函数,我们就补一个write函数:

#[no_mangle]pub extern "C" fn write(fd: c_int, buf: *const c_void, count: size_t) -> ssize_t {    unsafe { ocall::write(fd, buf, count) }}

这样,我们转接层对上模拟一个linux glibc的行为,对下转接到sgx特别实现,可让针对linux的编译的Rust应用程序跑在sgx内。

经验证的确如此,在添加了相应libc函数并拆掉一部分特殊代码后,我们enclave程序就运行起了。

std和sgx_tstd共存

上面提到,拆掉了一部分代码,主要这些代码依赖sgx_tstd里面特有功能,比如sgx_tstd::sgxfs::SgxFile。而开启原生std后sgx_tstd就因为rust的lang_item冲突而不能编译了。要想恢复使用这些功能,我们要么自己重新实现(copy)一份,要么让sgx_tstd和std共存。显然,后者更符合可持续发展原则。因此,我们给sgx_tstd打个补丁,让lang_item变成一个feature,不开启它就能与原生std共存了。

安全考虑

我们enclave程序是安全敏感的,如果一股脑将libc代理到ocall函数,显然是粗鲁的不安全的。因此,我们对每个代理的函数都会根据我们业务需求对其安全性做思考,调整其实现行为。

getrandom

随机数安全性尤其重要,直接关系到我们的密钥安全。rust的rand crate会调用getrandom函数来获取随机熵源。我们将getrandom函数代理到sgx_read_rand,sgx_read_rand在HW模式下会通过CPU硬件获取真随机数。实现如下:

#[no_mangle]pub extern "C" fn getrandom(buf: *mut c_void, buflen: size_t, flags: c_uint) -> ssize_t {    if buflen == 0 {        return 0;    }    let rv = unsafe { sgx_read_rand(buf as _, buflen) };    match rv {        sgx_status_t::SGX_SUCCESS => buflen as _,        _ => {            if flags & libc::GRND_NONBLOCK != 0 {                set_errno(libc::EAGAIN);            } else {                set_errno(libc::EINTR);            }            -1        }    }}

权限相关

支持了std,我们需要严格控制enclave内代码的权限,以最大限度降低安全风险。越权访问代码最好是在编译构建时阻拦下来,次之是在运行时限制越权访问。

转接层对权限的控制策略如下:

操作

限制策略

打开文件

禁止打开文件,运行时报错。

read/write

运行时限制只允许操作stdio相关fd。

网络操作

禁止编译,链接报错。

syscall

只允许SYS_getrandom,并代理到sgx_read_rand。其余call运行时报错。

创建线程

禁止编译,链接报错。

获取工作目录

返回“(unreachable)", 该值是linux-glibc定义的工作目录不可达时的返回值。

获取环境变量

禁止,永远返回空。

获取时间

代理到ocall::clock_gettime, 由业务代码注意不要信任获取到的时间。

dlsym动态加载函数

只允许getrandom,其余返回空。

mmap

允许其内存分配功能,代理到sgx的malloc。其余操作返回错误。

链接错误排查

由于我们现在的原生std依然存在部分功能缺失, 当我们引入新的依赖到SGX时,少数情况有可能会遇到链接错误,比如依赖中有网络操作,报了如下错误:

简单情况下,我们可以去检查源码,发现是哪部分功能引入了这个依赖。但很多情况下,其实我们看到的代码虽然会经过编译,但最终进入binary的只有其中一小部分,那些静态不可达的代码都会被编译器/链接器丢弃掉。因此,我们可能很难根据原始源码判断出实际生效的依赖关系。

因此,我们需要从最终输出的binary出发来分析上图中的connect究竟是怎么被引入进来的。我们写了一个callerfinder.py来辅助分析此类问题。

第一步先把这些undefined reference都用一个空的占位符号补上,使其能编译通过:

然后使用callerfinder库查找输出二进制文件中undefined函数的依赖关系:

In [1]: from callerfinder import CallerFinder
In [2]: finder = CallerFinder("./enclave/enclave.so")
In [3]: finder.print_callers('std::net::tcp::TcpStream::connect', 14)
std::net::tcp::TcpStream::connect_timeout::h79c6c1fec8ad56c5
  http_req::request::Request::send::h8ea00de7a9d4e562
    enclaveapp::create_attestation_report::h08c59df2ec69ab65
      enclaveapp::prpc_service::get_runtime_info::h5ee8ea7c8422d583
        phala_enclave_api::...::dispatch_request::hd1bf94703ec9513e
          ecall_prpc_request
            sgx_ecall_prpc_request

这样我们就很清晰地找到网络操作的来源,根据情况采取对应的措施,比如这里我们把http_req换成原http_req-sgx移植版本即可。

CPUID指令问题

我们将enclave代码迁移到std后用SGX_MODE=SW模式顺畅运行,SGX_MODE=HW环境下则出现多处崩溃。经排查这些崩溃均指向同一个函数rand::thread_rng(),而rand::thread_rng()其内部实现使用了std::is_x86_feature_detected宏来检测CPU对SIMD的支持程度。该宏使用了SGX环境禁止的CPUID指令,导致程序崩溃。

一方面SGX环境出于安全考虑禁止了CPUID指令,另一方面,应用程序使用CPUID检测CPU对SIMD的支持情况是很常见的“正当行为”。虽然CPUID触发崩溃虽然没有泄露信息,没有越权访问,没有触发Unsound等安全问题,是一种运行时安全守卫措施。但这种“正当行为”而触发运行时崩溃显然不能接受,如果我们代码依赖中有相关检测逻辑,在我们的业务随时有宕机风险。因此,我们要么让std::is_x86_feature_detected适配sgx环境,要么让保证我们整体代码不触及CPUID。

Teaclave的sgx_tstd中重新实现了is_x86_feature_detected宏来避免触及CPUID。而我们采用原生std的方案,问题就稍微复杂一点了,我们无法像前文那样通过libc重新实现is_x86_feature_detected(除非给std打补丁)。另外,单解决一个is_x86_feature_detected显然也不能避免代码直接内嵌CPUID汇编指令的情况。因此,我们暂且选择让代码不触及CPUID。

具体方法为,我们增加一个编译后处理步骤,通过反汇编输出的enclave.so来检查其中是否含有CPUID指令。如果有我们让make报错并打印出函数名:

然后可利用前述callerfinder.py找出哪些函数导致依赖了CPUID:

然后,我们可以顺藤摸瓜找到对应的代码实现。如果是必要的,我们可以patch对应的crate让他使用teaclave提供的is_x86_feature_detected, 比如rand::thread_rng();如果是能砍掉的功能我们就砍掉,比如上图中的env_logger我们href="github.com/Phala-Networ">关掉其regex feature即可消除此依赖。

关于其它SGX非法指令

既然CPUID存在此问题,那么是否可能碰到其它SGX特别禁止的指令呢?理论上当然是可能碰到的,从intel的指南看看还有哪些特殊指令。

指南描述一些SGX环境的非法指令如下:

对于其2和其3,除了INT/SYSCALL/SYSENTER等系统调用指令之外,其余都应只出现在系统内核代码中。而系统调用相关功能除非极其个性的程序,否则不论是Rust还是C/C++生态都应调用libc的相关函数或syscall函数来完成,而不是直接嵌入汇编指令。

我们着重需要关注1中这些指令:

  • CPUID 常用于检测CPU对SIMD的支持,以便使用不同SIMD指令集处理计算密集任务,需要关注。
  • GETSEC 是一个leaf funtion总入口,有很多子功能,都是特殊用途的。一般程序不会用到
  • RDPMC

读取性能计数器,特殊用途,perf之类的工具使用,不必关注。

  • RDTSC/RDTSCP

读取CPU timestamp计数器,可能被应用程序使用,加入指令检测脚本。

  • SGDT - Store Global Descriptor Table Register

仅操作系统使用

  • SIDT - Store Interrupt Descriptor Table Register

仅操作系统使用

  • SLDT - Store Local Descriptor Table Register

仅操作系统使用

  • STR - Store Task Register

仅操作系统使用

  • VMCALL/VMFUNC 虚拟化相关指令,不用关注

为保险起见,我们把这些指令全都加入后处理检查脚本中,禁止其使用。

小结

添加std支持后,我们用Rust开发SGX程序变得和开发普通Rust应用程序无太大差异,也能直接使用Rust标准工具链的单元测试等设施,开发效率上升一个台阶。

本文分享自微信公众号 - Rust语言学习交流(rust-china),作者:Kevin Wang

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-09-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 服务器TEE:百度Teaclave驱动安全计算生态

    Apache Teaclave (incubating) (https://teaclave.apache.org/)是号称全球首个通用安全计算平台。Teacl...

    安智客
  • 从可信执行环境到企业级大规模密文计算

    数字经济时代,数据是企业的核心资产,数据的全生命周期加密处理是保护企业数据核心资产的最有效最可靠手段之一。在信息安全的发展过程中,我们已经建立起国家乃至世界级的...

    安智客
  • Intel芯片架构中TEE的实现技术之SGX初探

    应该来说可信执行环境不仅仅指的是基于ARM的Trustzone技术的各种安全操作系统,还包括基于intel公司提出的SGX技术,以及虚拟化机制,比如L4微内核等...

    安智客
  • Rust China Conf 2021 首批议题确定

    10 月16日-17日,Rust China Conf 2021 将在上海举办,本次大会主题为“Rust the World”。在这一主题下,大会广泛接受海内外...

    MikeLoveRust
  • 【Rust日报】2019-11-21 主要使用 Rust 开发的 MesaTEE 正式进入 Apache 孵化器

    MesaTEE 是一个通用的安全计算框架,用于为安全关键场景提供通用计算服务。它结合了先进的混合内存安全(HMS)模型和可信计算技术(如 TPM )的能力,以及...

    MikeLoveRust
  • 蚂蚁区块链第10课 可信计算分类以及TEE硬件隐私合约链智能合约开发实践

    本文介绍可信计算分类INTEL SGX技术和ARM TRUSTZONE技术技术方案概要,以及应用INTEL SGX技术的蚂蚁区块链TEE硬件隐私链的智能合约开发...

    辉哥
  • TEE与比特币硬件钱包应用之Ledger

    近日Ledger ,一家专注于加密货币及区块链应用安全解决方案的法国创业公司,其已与英特尔公司达成合作,旨在为数字钱包用户提供更安全的解决方案,据悉...

    安智客
  • KubeTEE = Kubernetes + TEE

    云原生的概念这两年逐渐深入人心,越来越多的应用从设计之初就是面向云环境的。但是,据统计,仍然有很大一部分应用由于各种原因没有上云,尤其是那些较为重要、敏感的应用...

    CNCF
  • Intel芯片架构中TEE的实现技术之SGX(三)开发环境简介及搭建

    前述内容请参照 Intel芯片架构中TEE的实现技术之SGX初探(二) Intel芯片架构中TEE的实现技术之SGX初探 SGX技术是Intel于2013年在I...

    安智客
  • 跨越嵌入式到云端的新型容器:WebAssembly Micro Runtime

    2019 年 11 月,Mozilla、英特尔、RedHat 和 Fastly 公司宣布成立字节码联盟(Bytecode Alliance),英特尔的 WebA...

    深度学习与Python
  • MIT研究人员正研发基于RISC-V的安全芯片方案

    RISC-V,以及其所代表开源芯片发展趋势在近年获得了极大关注。加州大学柏克莱分校,麻省理工很早就投入RISC-V研究。

    安智客
  • 【Rust blog】Rust + Flutter 高性能的跨端尝试

    稍作配置,同一份代码横跨 Android & IOS,相比于 React Native 方案更加高性能。除此之外,得益于 Rust 跨平台加持,Rust 部分的...

    MikeLoveRust
  • 微软将为Linux 操作系统带来TEE的支持

    Google都在积极布局TEE,都积极发展自己的TEEOS,微软也不会缺席,将为Linux 操作系统带来“可信执行环境”的支持,为机密计算提供安全保障机制,并且...

    安智客
  • 基于TEE的共享学习:数据孤岛解决方案

    随着人工智能的兴起,数据的质量和数量,已经成为影响机器学习模型效果最重要的因素之一,因此通过数据共享的模式来“扩展”数据量、从而提升模型效果的诉求也变得越发强烈...

    安智客
  • # 如何安全地保存密码?

    如今的互联网生活,让每个人都离不开密码 — 操作系统有开机密码(用户密码),各种应用有登录密码,甚至还有交易密码。形形色色的密码让用户头皮发麻,要么使用重复的不...

    tyrchen
  • C++ 动态新闻推送 第25期

    从reddit/hackernews/lobsters/meetingcpp摘抄一些c++动态。

    王很水
  • 共享学习:蚂蚁金服提出全新数据孤岛解决方案

    随着人工智能的兴起,数据的质量和数量,已经成为影响机器学习模型效果最重要的因素之一,因此通过数据共享的模式来「扩展」数据量、从而提升模型效果的诉求也变得越发强烈...

    Python数据科学
  • 拿什么保护你,我的区块链

    ? 被纳入新基建的区块链,以数据不可篡改、可公开监管、便于查证的特性,广泛应用于有多方参与的系统中,为多方交互的信息(行为、数据等)提供可靠的存证。那么,在信...

    腾讯技术工程官方号
  • hysc - flow:基于sgx大数据分析框架的隐私保护基因组计算

    可信执行环境(TEE),如Intel的软件守卫扩展(SGX),已被广泛研究,以提高计算敏感数据(如人类基因组学)的安全性和隐私保护。然而,SGX经常会产生性能障...

    用户8789655

扫码关注云+社区

领取腾讯云代金券