东亚银行、岚图汽车带你解锁 AIGC 时代的数字化人才培养各赛道新模式! 了解详情
写点什么

Firefox 是怎样解决内存安全的?

  • 2020-03-10
  • 本文字数:2784 字

    阅读完需:约 9 分钟

Firefox是怎样解决内存安全的?


对于像 Firefox 这样复杂且高度优化的系统,内存安全是最大的安全挑战之一。Firefox 主要是用 C 和 C++编写的。众所周知,这些语言很难安全地使用,因为任何错误都有可能导致程序完全崩溃。


Firefox 软件工程师 Nathan Froyd 写道,“我们努力寻找并消除内存风险,但也在改进 Firefox 代码库,以便在更深的层次上解决这些问题。”


截至目前,Firefox 主要关注两项技术:


  1. 将代码分解成多个沙箱进程,减少特权

  2. 用一门安全的语言去重写代码,比如 Rust

一种新方法

“虽然我们继续在 Firefox 中使用沙箱和 Rust,但它们各有局限性。对已有的大型组件,进程级沙箱很有效,但这会消耗大量系统资源,因此必须谨慎使用。”Nathan Froyd 写道。


虽然 Rust 是轻量级的,但是重写现有的数百万行 C++代码是一件“劳神费力”的事。


Graphite字形库为例,Firefox 用它来正确呈现某些复杂字体。它太小了,不适合“放入”自己的进程中。


然而,如果发现内存风险,即使是站点隔离的进程架构也无法阻止恶意字体破坏加载它的页面。同时,重写和维护这种领域专用的代码并不是 Firefox 有限工程资源的理想用法。


如今,Firefox 在“军火库”中加入第三种方法。


加利福尼亚大学、圣地亚哥大学、德克萨斯大学、奥斯汀分校和斯坦福大学的研究人员开发出一种新的沙箱技术,叫RLBox


Nathan Froyd 表示,“它让我们能快速有效地将现有 Firefox 组件转换为在一个 WebAssembly 沙箱中运行。我们已经成功地将该技术集成到我们的代码库中,并将其用于沙箱化 Graphite。”


据悉,这种隔离将提供给 Firefox 74 的 Linux 用户和 Firefox 75 的 Mac 用户,不久之后还将提供 Windows 支持。

构建一个 wasm 沙箱

Wasm 沙箱背后的核心实现思想是,你可以将 C/C++编译成 wasm 代码,然后将该 wasm 代码编译成实际运行程序的机器的本机代码。


这些步骤与在浏览器中运行C/C++应用程序的步骤类似,但是,“我们在构建 Firefox 本身之前,就执行本地代码到 wasm 的转换。这两个步骤都各自依赖于重要的软件,我们还添加了第三个步骤,以使沙箱转换更简单、更不易出错。”Nathan Froyd 写道。


首先,你要将 C/C++编译成 wasm 代码。作为 WebAssembly 工作的一部分,在Clang和LLVM中添加一个 wasm 后端。光有一个编译器是不够的;你还需一个 C/C++的标准库。该组件是通过wsi -sdk提供的。一旦拥有这些组件,你就有足够能力将 C/C++转化成 wasm 代码。


其次,你需要将 wasm 代码转换为本机对象文件。Nathan Froyd 说,“当我们第一次实现 wasm 沙箱时,经常有人问我们,‘为什么需要这个步骤?’你可以分发 wasm 代码,并在 Firefox 启动时在用户的机器上动态编译它。我们本可以做到这一点,但这种方法要求针对每个沙箱实例重新编译 wasm 代码。“


在每个源都位于单独进程中的情况下,每个沙箱都编译代码是不必要的重复。他们选择的方法支持在多个进程间共享已编译的本机代码,从而能节省大量内存。


这种方法还提高了沙箱的启动速度,这对于细粒度的沙箱非常重要,例如,将每次字体访问或图像加载的相关联代码置入沙箱。

通过 Cranelift 实现预编译

这种方法并不意味着必须自己编写将 wasm 代码编译成本机代码的编译器。


“我们用相同的编译器后端实现这种提前编译”,它最终将通过字节码联盟的Lucet编译器和运行时来支持 Firefox JavaScript 引擎的 wasm 组件:Cranelift


这种代码共享可确保 JavaScript 引擎和 wasm 沙箱编译器共享改进所带来的好处。由于工程原因,这两段代码目前使用不同版本的 Cranelift。


然而,随着沙箱技术的成熟,“我们希望修改它们以使用完全相同的代码库”。


现在,Firefox 工程师已将 wasm 代码转换为本机对象代码,“我们需要能从 C++调用沙箱代码”。如果沙箱代码在单独的虚拟机中运行,这一步将涉及到在运行时查找函数名以及管理与虚拟机相关的状态。


但是,通过上面设置,沙箱代码是符合 wasm 安全模型的本机编译代码。因此,可以使用与调用常规本机代码相同的机制来调用沙箱函数。


“我们必须注意所涉及的不同机器模型:wasm 代码使用 32 位指针,而我们最初的目标平台 x86-64 Linux 使用 64 位指针。但是,还有其他障碍需要克服,这就把我们带到转换过程的最后一步。”Nathan Froyd 写道

确保沙箱正确

使用与常规本机代码相同的机制调用沙箱代码很方便,但它隐藏了一个重要细节。“我们不能相信任何来自沙箱的东西,因为对手可能已经损害沙箱”。


例如,有个沙箱函数:


/* 返回0到16之间的值。  */
int return_the_value();
复制代码


不能保证这个沙箱函数遵循它的契约。因此,“要确保返回的值落在我们期望范围内”。


类似地,对于一个返回指针的沙箱函数:


extern const char* do_the_thing();
复制代码


Nathan Froyd 表示,“我们不能保证返回的指针实际上指向沙箱控制的内存。对手可能会强迫返回的指针指向应用程序沙箱之外的某个地方。因此,我们在使用指针前要验证它。”


在阅读源代码时,还有一些其他的运行时约束并不明显。


例如,上面返回的指针可能指向沙箱中动态分配的内存。在这种情况下,应该由沙箱释放指针,而不是由主机应用程序释放。“我们可以依靠开发人员始终记住哪些值是应用程序值,哪些值是沙箱值”。


经验表明,这种方法是不可行的。

污染数据

上面两个例子说明一个普遍原则:从沙箱返回的数据应该被明确标识。有了这个标识,我们就可以确保以适当方式处理数据。


我们将与沙箱相关的数据标记为“污染”。污染数据可以自由地操作(例如指针运算、访问字段),生成更多污染数据。


但是,当我们将污染数据转换为非污染数据时,我们希望这些操作尽可能明确。污染数据不仅对管理从沙箱返回的内存很有价值,它对于识别从沙箱中返回的可能需要额外验证的数据也很有价值,例如指向某个外部数组的索引。


因此,我们将沙箱中所有公开的函数建模为返回污染数据。这些函数还将污染数据作为参数,因为它们所操作的任何东西在某种程度上都必须属于沙箱。


一旦函数调用有了这个接口,编译器就变成了一个污染检查器。当污染数据在需要非污染数据的上下文中使用时,编译器将发生错误,反之亦然。


这些上下文正是需要传播污染数据和/或需要验证数据的地方。RLBox处理污染数据的所有细节,并提供一些特性,可以直接将库的接口增量转换为沙箱接口。

下一步工作

有了 wasm 沙箱的核心基础结构,我们就可以集中精力提高它在 Firefox 代码库中的影响力了——既可以将它带到所有支持的平台上,也可以将它应用到更多的组件上。


由于这种技术是轻量级的,并且易于使用,我们希望在接下来的几个月里对 Firefox 的更多部件进行快速沙箱化。


我们最初的努力集中在与 Firefox 绑定的第三方库上。此类库通常具有定义良好的入口点,并且不会与系统的其他部分广泛共享内存。然而,在未来,我们也计划将这项技术应用于甲方代码。


关于作者


Nathan Froyd 是 Firefox 的软件工程师。在业余时间,他喜欢奥林匹克举重和阅读。


英文原文:


Securing Firefox with WebAssembly


2020-03-10 07:301743
用户头像

发布了 687 篇内容, 共 396.2 次阅读, 收获喜欢 1498 次。

关注

评论 1 条评论

发布
用户头像
曾firefox的忠实用户,但内存泄漏问题让人困惑啊.
2020-03-11 10:27
回复
没有更多了
发现更多内容

Alibaba技术大牛丢给我一份Spring Cloud笔记,在GitHub的热度居然高达81.6k标星,太强了!

Java架构之路

Java 程序员 架构 面试 编程语言

问题篇:WSL和VMware。你怎么选择(附wsl安装步骤)

小Q

Java Linux 学习 架构 面试

week1 架构方法-作业-杨斌

杨斌

自我管理系列-为啥要做生产问题分析报告呀

罗小龙

复盘 IT, 经验总结 职场搞笑 生产事故

视频客服的应用和优点

anyRTC开发者

音视频 WebRTC 直播 RTC

滴滴高峰期亿级并发如何调优?Java亿级并发系统架构设计手册分享

Java架构追梦

Java 架构 面试 高并发 亿级流量

保险区块链创新中心成立,三方面赋能行业数字化转型

CECBC

区块链 保险

京东智联云4篇论文入选国际语音顶级大会Interspeech 2020

京东科技开发者

人工智能 大数据 语音识别

15张图解Redis为什么这么快

Java架构师迁哥

服了!不愧是AlibabaP8级别的大牛,把Kafka的精髓全部总结整理成了一份“限量笔记”。

Java架构之路

Java 程序员 架构 面试 编程语言

面试阿里P6,过关斩将直通2面,结果3面找了个架构师来吊打我?

Java架构之路

Java 程序员 架构 面试 编程语言

Hbase实用技巧:全量+增量数据的迁移方法

华为云开发者联盟

数据 HBase 集群

端应用研发进入云原生时代

移动研发平台EMAS

求职时这样回答问题你就输了!来自IT类面试官视角的深度解读

Java架构师迁哥

如何实现微服务架构下的分布式事务?

华为云开发者联盟

架构 分布式 事务

vim快捷键收藏版

良知犹存

vim

接口文档生成工具

测试人生路

接口文档

架构师训练营 1 期 -- 第六周作业

曾彪彪

极客大学架构师训练营

企业CRM未来发展趋势展望

Philips

敏捷开发 软件架构

.NET可视化权限功能界面设计

雯雯写代码

只有基于区块链才可能实现“大众创业、万众创新”

CECBC

区块链 分布式技术

同学信誓旦旦地说,我司的系统从来不做性能调优!

沉默王二

Java 性能调优

陈山枝:5G+车联网 推动中国特色车路协同发展

想了解Webpack,看这篇就够了

华为云开发者联盟

华为 大前端 开发

LeetCode题解:78. 子集,递归回溯,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

假的数字人民币钱包已出现,真的是啥样?

CECBC

数字货币 数字钱包

信息量爆炸!78天闭门深造1258页SpringCloud学习进阶笔记,再战蚂蚁金服

996小迁

Java 架构 面试 SpringCloud

VRBT视频彩铃解决方案

dwqcmo

5G 音视频 解决方案

中台:未到终局,焉知生死?

ToB行业头条

中台

"全能"人才的概念真的对吗

supernova

创业 读书笔记 随笔杂谈

杰哥获奖了!

JackTian

Linux 程序人生 运维工程师 运维人生

Firefox是怎样解决内存安全的?_安全_Nathan Froyd_InfoQ精选文章