存在一种完美的编程语言吗?
Rust 语言因其并发安全性而深受众多开发者的喜爱,曾在多个榜单上获评最受欢迎编程语言。然而,现在有人花费大量时间编写 10 万行 Rust 代码之后,撰写博客阐明 Rust 语言的一系列缺点,以下是博客的主要内容。 我深入研究 Rust 是为了改进由 Xobs 编写的 Xous 操作系统。Xous 是一个用纯 Rust 编写的微内核消息传递操作系统,是为了轻量级 (IoT / 嵌入式规模) 的安全优先平台(例如 Precursor)而编写的,用于 MMU 的硬件强制型页面级内存保护。
一年来,我们为 Xous 操作系统添加了许多功能,包括网络 (TCP/UDP/DNS)、用于模态和多语言文本的中间件图形抽象、存储(以加密的形式)、PDDB、可信启动(trusted boot)以及密钥管理库等。
我们决定编写自己的操作系统而不是使用 SeL4、Tock、QNX 或 Linux 等现有实现,是因为我们想真正了设备中每一行代码都在做什么。特别是对于 Linux,它的源代码库非常庞大且动态,即使开源,也不可能搞清其内核中的每一行代码。
因此,Xous 仅支持我们的平台,以尽可能避免内核不必要的复杂性。 这样减少应用范围还意味着我们还可以充分利用 CPU 在 FPGA 中运行的优势 。因此,Xous 以一种不寻常的 RV32-IMAC 配置为目标:具有 MMU + AES 扩展的配置。 FPGA 意味着我们有能力在硬件级别上修复 API 错误,从而使内核更加精简。
这对于从 RAM 中处理诸如挂起和恢复之类的抽象破坏(abstraction-busting)进程尤其重要。 我们创建 Xous 时研究了大量的系统编程语言,最终 Rust 脱颖而出。当时它刚刚开始支持 `no-std`,它的特点是强类型、内存安全,具有良好的工具和新型生态系统。我个人是强类型语言的忠实拥护者,而内存安全性不仅有利于系统编程,还能使优化器更好地生成代码,并且 Rust 适用于并发。
实际上,我希望 Precursor 有一个支持标记指针和内存功能的 CPU,类似于 CHERI。于是我们和 CHERI 研发团队进行了一些讨论,但显然他们非常专注于 C 语言,也没有足够的带宽来支持 Rust。
总体而言,C 比 Rust 需要 CHERI 多得多,他们的选择是符合资源优先原则的。我们不使用 C 语言,但出于安全性考虑,我希望有一天 Rust 中会存在硬件强制型胖指针(fat pointer)。
然而,Rust 语言绝不是完美的,甚至给我们的开发带来了很多问题。下面我列举一下 Rust 的缺点。
语法混乱复杂
我发现 Rust 语法密集、繁重且难以阅读,例如:
再比如,Rust 宏的可读性也存在问题——即使是我自己编写的一些 Rust 宏也「只是勉强工作」。
一种可靠的语言不应该存在这些语法问题。 Rust 的确很强大,它的标准库中包含 HashMaps、Vecs 和 Threads 等数据结构,丰富且可用性高。然而,Rust 的「std」库并没有为我们构建可审计的代码库带来任何好处。
Rust 不够完善
我们编写 Xous 的代码时,引入了一个叫作「const generic」的新类型。在此之前,Rust 没有原生能力来处理多于 32 个元素的数组,这个限制令人抓狂。 在编写 Xous 的过程中,Rust 的内联汇编、工作空间等功能逐渐成熟,这意味着我们需要重新审视已经写好的代码,以使关键的初始启动代码集成进我们构建的系统。
Xous 开发的第一年都是使用’no-std’完成的,代价是占用大量内存空间且复杂性高。尽管可以编写一个只有预先分配的、静态大小的数据结构的操作系统,但为了适应最坏情况下的元素数量,因此我们不得不推出一些自己的数据结构。 大约一年前,Xobs 将 Rust 的 `std` 库移植到 Xous,这意味着我们可以在稳定的 Rust 中访问堆,现在 Xous 与特定版本的 Rust 绑定。 `std` 库从根本上将内存分配、线程创建等「不安全」的硬件结构转变成了「安全」的 Rust 结构。
然而,我必须不断提醒自己,拥有 `std` 库并不能消除关键代码中的安全漏洞风险——它只是将许多关键代码移动到标准库中。 Rust 有固定的更新周期,这意味着我们也必须定期更新 Xous ,以保持与语言的兼容性。 但这可能是不可持续的。
最终,我们需要锁定代码库,但我没有明确的退出策略。也许我们可以考虑仍然使用 `no-std` 以获得稳定的 `alloc` 功能来访问堆。但这样我们就还需要使用 Vec、HashMap、Thread 和 Arc/Mutex/Rc/RefCell/Box 构造等,以使 Xous 能够被有效编码。
Rust 在供应链安全方面堪忧
在 rustup.rs 安装文件中有如下代码:
Crates.io 也存在一种拼写错误,很难确定哪些 crate 是好或坏;一些完全按照用户想要的名称命名的 crate 放弃提供所需功能,而积极维护的 crate 必须采用不太直观的名称。当然,这不是 Rust 独有的问题。 还有一个事实是,依赖项是链式的。也就是说当你从 crates.io 拉入一个东西时,你也会拉入该 crate 的所有从属依赖项,以及它们所有的 build.rs (http://build.rs/) 脚本,这些最终都将在你的机器上运行。
因此,仅审核 Cargo.toml 文件中明确指定的 crate 是不够的——您还必须审核所有相关 crate 是否存在潜在的供应链攻击。 幸运的是,Rust 确实允许您使用 Cargo.lock 文件将 crate 固定在特定版本,并且可以完全指定依赖 crate 。我们试图在 Xous 中通过发布 Cargo.lock 文件并将我们所有的一阶相关 crate 指定为次要修订的策略来缓解这个问题。
然而,我们的大部分调试和测试框架都依赖于一些相当花哨和复杂的 crate,这些 crate 引入了大量的依赖项,即使我尝试为我们的目标硬件运行构建,在主机上运行的依赖 crate 和 build.rs 脚本还是被构建。 针对这个问题,我编写了一个名为「crate-scraper」的小工具,它为我们的 Cargo.toml 文件中指定的每个源下载源包,并且将它们存储在本地,这样我们就可以获得用于构建 Xous 版本的代码快照。
它还运行一个快速的「分析」程序——搜索名为 build.rs 的文件并将它们整理到一个文件中,这样我就可以更快地通过 grep 查找明显的问题。
当然,手动审查并不是检测嵌入在 build.rs (http://build.rs/) 文件中巧妙伪装的恶意软件的实用方法,但它至少让我了解了我们正在处理的攻击面的规模。令人惊讶的是,我们审查出来自各种第三方的大约 5700 行代码,用于操作文件、目录和环境变量,并在我的计算机上运行其他程序。 我不确定这个问题是否有更好的解决方案,但是,如果你的目标是构建可信赖的固件,请警惕 Rust 广泛的软件供应链攻击面。
无法复现别人的 Rust 构建
我对 Rust 的最后一点看法是,一台计算机上的构建无法在另一台上复现。我认为这主要是因为 Rust 将源代码的完整路径作为内置到二进制文件中调试字符串的一部分。
这导致了一些糟糕的情况,例如我们在 Windows 上构建的工作成功了,但在 Linux 下却失败了,因为二者的路径名非常不同,这会导致一些内存对象在目标内存中被转移。 公平地讲,这些失败是由于 Xous 中存在错误,这些错误已经得到修复。
但是,最终仍会有用户向我们报告我们无法复现,因为他们在构建系统上的路径与我们的不同。
最后,我想说尽管这里列出了所有的怨言,但如果能重来,Rust 仍然是我们用于构建 Xous 所用语言的有力竞争者。我用 C、Python 和 Java 完成了很多大型项目,所有这些项目最终都背负着「不断增加的技术债务」,而 Rust 可以规避这些问题。
审核编辑 :李倩
Rust 语言因其并发安全性而深受众多开发者的喜爱,曾在多个榜单上获评最受欢迎编程语言。然而,现在有人花费大量时间编写 10 万行 Rust 代码之后,撰写博客阐明 Rust 语言的一系列缺点,以下是博客的主要内容。 我深入研究 Rust 是为了改进由 Xobs 编写的 Xous 操作系统。Xous 是一个用纯 Rust 编写的微内核消息传递操作系统,是为了轻量级 (IoT / 嵌入式规模) 的安全优先平台(例如 Precursor)而编写的,用于 MMU 的硬件强制型页面级内存保护。
一年来,我们为 Xous 操作系统添加了许多功能,包括网络 (TCP/UDP/DNS)、用于模态和多语言文本的中间件图形抽象、存储(以加密的形式)、PDDB、可信启动(trusted boot)以及密钥管理库等。
我们决定编写自己的操作系统而不是使用 SeL4、Tock、QNX 或 Linux 等现有实现,是因为我们想真正了设备中每一行代码都在做什么。特别是对于 Linux,它的源代码库非常庞大且动态,即使开源,也不可能搞清其内核中的每一行代码。
因此,Xous 仅支持我们的平台,以尽可能避免内核不必要的复杂性。 这样减少应用范围还意味着我们还可以充分利用 CPU 在 FPGA 中运行的优势 。因此,Xous 以一种不寻常的 RV32-IMAC 配置为目标:具有 MMU + AES 扩展的配置。 FPGA 意味着我们有能力在硬件级别上修复 API 错误,从而使内核更加精简。
这对于从 RAM 中处理诸如挂起和恢复之类的抽象破坏(abstraction-busting)进程尤其重要。 我们创建 Xous 时研究了大量的系统编程语言,最终 Rust 脱颖而出。当时它刚刚开始支持 `no-std`,它的特点是强类型、内存安全,具有良好的工具和新型生态系统。我个人是强类型语言的忠实拥护者,而内存安全性不仅有利于系统编程,还能使优化器更好地生成代码,并且 Rust 适用于并发。
实际上,我希望 Precursor 有一个支持标记指针和内存功能的 CPU,类似于 CHERI。于是我们和 CHERI 研发团队进行了一些讨论,但显然他们非常专注于 C 语言,也没有足够的带宽来支持 Rust。
总体而言,C 比 Rust 需要 CHERI 多得多,他们的选择是符合资源优先原则的。我们不使用 C 语言,但出于安全性考虑,我希望有一天 Rust 中会存在硬件强制型胖指针(fat pointer)。
然而,Rust 语言绝不是完美的,甚至给我们的开发带来了很多问题。下面我列举一下 Rust 的缺点。
语法混乱复杂
我发现 Rust 语法密集、繁重且难以阅读,例如:
Trying::<&'aheavy>(syntax,|like|{this.can_be(maddening)}).map(|_|())?;
简单来说,上面的代码类似于在对象(实际上是 `struct`)上调用一个名为「to_read」的方法。 还有一种不遵循 Rust 语法规则的宏和指令也能运行:
#[cfg(all(not(baremetal),any(feature=“hazmat”,feature=“debug_print”)))]
上面的语句中最令我困惑的是使用‘=’来表示等价而不是赋值,因为配置指令中的内容不是 Rust 代码,它就像一个完全独立的元语言。再比如,Rust 宏的可读性也存在问题——即使是我自己编写的一些 Rust 宏也「只是勉强工作」。
一种可靠的语言不应该存在这些语法问题。 Rust 的确很强大,它的标准库中包含 HashMaps、Vecs 和 Threads 等数据结构,丰富且可用性高。然而,Rust 的「std」库并没有为我们构建可审计的代码库带来任何好处。
Rust 不够完善
我们编写 Xous 的代码时,引入了一个叫作「const generic」的新类型。在此之前,Rust 没有原生能力来处理多于 32 个元素的数组,这个限制令人抓狂。 在编写 Xous 的过程中,Rust 的内联汇编、工作空间等功能逐渐成熟,这意味着我们需要重新审视已经写好的代码,以使关键的初始启动代码集成进我们构建的系统。
Xous 开发的第一年都是使用’no-std’完成的,代价是占用大量内存空间且复杂性高。尽管可以编写一个只有预先分配的、静态大小的数据结构的操作系统,但为了适应最坏情况下的元素数量,因此我们不得不推出一些自己的数据结构。 大约一年前,Xobs 将 Rust 的 `std` 库移植到 Xous,这意味着我们可以在稳定的 Rust 中访问堆,现在 Xous 与特定版本的 Rust 绑定。 `std` 库从根本上将内存分配、线程创建等「不安全」的硬件结构转变成了「安全」的 Rust 结构。
然而,我必须不断提醒自己,拥有 `std` 库并不能消除关键代码中的安全漏洞风险——它只是将许多关键代码移动到标准库中。 Rust 有固定的更新周期,这意味着我们也必须定期更新 Xous ,以保持与语言的兼容性。 但这可能是不可持续的。
最终,我们需要锁定代码库,但我没有明确的退出策略。也许我们可以考虑仍然使用 `no-std` 以获得稳定的 `alloc` 功能来访问堆。但这样我们就还需要使用 Vec、HashMap、Thread 和 Arc/Mutex/Rc/RefCell/Box 构造等,以使 Xous 能够被有效编码。
Rust 在供应链安全方面堪忧
在 rustup.rs 安装文件中有如下代码:
`curl--proto'=https'--tlsv1.2-sSfhttps://sh.rustup.rs|sh`
用户可以下载脚本并在运行之前对其进行检查,这似乎比 vscode 的 Windows .MSI 安装程序好得多。但是,这种做法遍及整个构建生态系统,让我对通过 crates.io 生态系统发起的软件供应链攻击的可能性感到不安。 Crates.io 也存在一种拼写错误,很难确定哪些 crate 是好或坏;一些完全按照用户想要的名称命名的 crate 放弃提供所需功能,而积极维护的 crate 必须采用不太直观的名称。当然,这不是 Rust 独有的问题。 还有一个事实是,依赖项是链式的。也就是说当你从 crates.io 拉入一个东西时,你也会拉入该 crate 的所有从属依赖项,以及它们所有的 build.rs (http://build.rs/) 脚本,这些最终都将在你的机器上运行。
因此,仅审核 Cargo.toml 文件中明确指定的 crate 是不够的——您还必须审核所有相关 crate 是否存在潜在的供应链攻击。 幸运的是,Rust 确实允许您使用 Cargo.lock 文件将 crate 固定在特定版本,并且可以完全指定依赖 crate 。我们试图在 Xous 中通过发布 Cargo.lock 文件并将我们所有的一阶相关 crate 指定为次要修订的策略来缓解这个问题。
然而,我们的大部分调试和测试框架都依赖于一些相当花哨和复杂的 crate,这些 crate 引入了大量的依赖项,即使我尝试为我们的目标硬件运行构建,在主机上运行的依赖 crate 和 build.rs 脚本还是被构建。 针对这个问题,我编写了一个名为「crate-scraper」的小工具,它为我们的 Cargo.toml 文件中指定的每个源下载源包,并且将它们存储在本地,这样我们就可以获得用于构建 Xous 版本的代码快照。
它还运行一个快速的「分析」程序——搜索名为 build.rs 的文件并将它们整理到一个文件中,这样我就可以更快地通过 grep 查找明显的问题。
当然,手动审查并不是检测嵌入在 build.rs (http://build.rs/) 文件中巧妙伪装的恶意软件的实用方法,但它至少让我了解了我们正在处理的攻击面的规模。令人惊讶的是,我们审查出来自各种第三方的大约 5700 行代码,用于操作文件、目录和环境变量,并在我的计算机上运行其他程序。 我不确定这个问题是否有更好的解决方案,但是,如果你的目标是构建可信赖的固件,请警惕 Rust 广泛的软件供应链攻击面。
无法复现别人的 Rust 构建
我对 Rust 的最后一点看法是,一台计算机上的构建无法在另一台上复现。我认为这主要是因为 Rust 将源代码的完整路径作为内置到二进制文件中调试字符串的一部分。
这导致了一些糟糕的情况,例如我们在 Windows 上构建的工作成功了,但在 Linux 下却失败了,因为二者的路径名非常不同,这会导致一些内存对象在目标内存中被转移。 公平地讲,这些失败是由于 Xous 中存在错误,这些错误已经得到修复。
但是,最终仍会有用户向我们报告我们无法复现,因为他们在构建系统上的路径与我们的不同。
最后,我想说尽管这里列出了所有的怨言,但如果能重来,Rust 仍然是我们用于构建 Xous 所用语言的有力竞争者。我用 C、Python 和 Java 完成了很多大型项目,所有这些项目最终都背负着「不断增加的技术债务」,而 Rust 可以规避这些问题。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。
举报投诉
-
cpu
+关注
关注
68文章
10835浏览量
211315 -
操作系统
+关注
关注
37文章
6757浏览量
123229 -
编程语言
+关注
关注
10文章
1941浏览量
34641
原文标题:编写完10万行代码,我发了篇长文吐槽Rust
文章出处:【微信号:CVSCHOOL,微信公众号:OpenCV学堂】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
编程语言的误区与常见问题
: 选择编程语言时,应考虑项目需求、个人兴趣以及语言的适用性。例如,如果你对Web开发感兴趣,那么学习JavaScript可能是一个好选择。 认识到没有
C语言与其他编程语言的比较
C语言作为一种历史悠久的编程语言,自其诞生以来,一直在软件开发领域扮演着重要角色。它以其高效、灵活和可移植性强的特点,成为了系统级
一种无透镜成像的新方法
使用OAM-HHG EUV光束对高度周期性结构进行成像的EUV聚光显微镜 为了研究微电子或光子元件中的纳米级图案,一种基于无透镜成像的新方法可以实现近乎完美的高分辨率显微镜。 层析成像是一种强大的无
abb工业机器人的编程语言是什么
ABB工业机器人的编程语言主要是RAPID(Robot Application Programming Interface for Development),它是一种高级编程
cnc系统一般可用几种编程语言
。CNC系统广泛应用于机械制造、汽车制造、航空航天等领域。 CNC系统的编程语言是实现CNC系统控制功能的关键技术之一。以下是对CNC系统可用编程语
fpga三种编程语言
FPGA(现场可编程门阵列)的编程涉及到三种主要的硬件描述语言(HDL):VHDL(VHSIC Hardware Description Language)、Verilog以及Syst
verilog与其他编程语言的接口机制
Verilog是一种硬件描述语言,用于描述数字电路的行为和结构。与其他编程语言相比,Verilog具有与硬件紧密结合的特点,因此其接口机制也有一
plc编程语言与c语言的联系 c语言和PLC有什么区别
PLC编程语言与C语言的联系 PLC(可编程逻辑控制器)是一种针对自动化控制系统的特殊计算机。PLC编程
plc编程语言有几种,一般常用有哪些
PLC(可编程逻辑控制器)是一种用于自动化控制系统的电子设备,它使用特定的编程语言来控制和监视机器或过程。常见的PLC编程
评论