扫码打开虎嗅APP
本文来自微信公众号:电子工程世界 (ID:EEworldbbs),作者:付斌
还记得去年美国CISA、NSA、FBI联合澳大利亚、加拿大、英国和新西兰的网络安全机构点名C/C++吗?那时候,各国机构认为C/C++存在内存安全漏洞,建议人们放弃使用这个编程语言,并改用Rust。
时隔8个月,这些机构不仅没有改变自己的想法,反而变本加厉了。
这几天,美国国防高级研究计划局(DARPA)就又整了个“大活”,准备用最近特别火热的AI,来替换掉现在的C语言代码,以迁移到Rust。
AI要改变你的代码
DARPA推动的项目叫TRACTOR(Translating All C TO Rust),这是一种编程代码转换工具。它开发机器学习工具,可以自动将老的C代码转换为Rust。
最终实现的效果就是,你可以去任何一个LLM网站,与其中一个AI聊天机器人聊天,你只需要说“这里有一些C代码,请将其翻译成安全的惯用Rust代码”,然后再复制粘贴就好了。
之所以DARPA要做这么一个工具,是因为他们认为内存安全错误(例如缓冲区溢出)是大型代码库中的主要漏洞,DARPA希望AI模型可以帮助编程语言翻译,以使软件更加安全。
不过他们也承认,把C代码翻译成Rust有挑战。
一是两种编程语言的程序结构差异比较大,大语言模型可能会给出一些表面上看起来”令人惊讶的好答案”,但这会令人产生错觉,因为这些可能是错误的答案。
二是C允许代码使用指针来进行各种任务,Rust则是禁止的,弥合这一差距需要的不仅仅是从C机械地翻译到Rust。
虽然有挑战,不过DARPA却异常努力。TRACTOR的目标不仅是实现代码转换的自动化,而且要实现熟练开发人员手动编写Rust代码的高质量和风格。
当前,DARPA已公开发布这一计划,也希望有更多的参赛者参与进来提交关于LLM支持的解决方案。
内存漏洞,不是不可控
内存安全漏洞(CWE-1399:综合分类:内存安全)是一类影响在编程语言中以意外方式访问、写入、分配或释放内存的漏洞。
透过漏洞,恶意行为者能够非法访问数据、损坏数据或运行任意恶意代码。例如,恶意行为者可能会向应用程序发送精心制作的有效载荷,从而破坏应用程序的内存,然后使其运行恶意软件。或者,恶意参与者可以发送包含恶意软件的格式错误的映像文件,以在受害者系统上创建交互式外壳。如果参与者可以以这种方式执行任意代码,参与者可以获得对运行该软件的帐户的控制权。
内存漏洞多,是现在机构和厂商所诟病的主要原因,比如说:
微软在2019年一次会议上表示,自2006年至2018年,70%的漏洞是由内存安全问题引起的;
在GoogleChromium项目中发现的漏洞中,约70%是内存安全漏洞;
在对Mozilla漏洞的分析中,34个严重/高度错误中有32个是内存安全漏洞;
根据Google Project Zero团队的分析,2021年有67%的零日漏洞是内存安全漏洞。
对C/C++来说,内存漏洞不是致命的,致命的是编码人员难免会出现这种错误。
作为核心焦点之一的C++,C++之父Bjarne Stroustrup也曾解释过:“很多人把焦点都放在C/C++的弱点上,但实际这些缺陷是完全可以避免的。C和C++根本就是天壤之别的两种语言,在30年进步中,C++正引入更多特性,变得越来越安全,合格的C++程序员可以写出安全性可以与Rust媲美的程序。忽视安全问题会伤害C++社区的大部分成员,并破坏我们为改进C++所做的许多其他工作。专注于安全也是如此。“言外之意,就是安全性不等于内存安全。
为了减少内存漏洞,许多软件制造商投资于开发人员的培训计划,许多公司也针对这种漏洞,进行了许多优化。
比如说引入了代码覆盖测试、安全编码准则、Fuzzing测试软件及开发者使用了静态应用程序安全测试(SAST)和动态应用程序安全测试(DAST)工具来查找各种软件的内存安全漏洞。
再比如,C++社区一直在考虑向后兼容性、内存安全默认值和基础语言的其他优先级之间的平衡;苹果修改了iBoot system中使用的C编译器工具链,以缓解内存和键入安全问题;微软早些年还开源了一个更安全的C语言版本Checked C,在C中添加静态和动态检查,以检测或防止常见的编程错误;Google打造了一款C++的继任者Carbon,针对现有代码的C++内存安全采取了改进措施。
硬件厂商,也在积极开发使用硬件支持内存保护。比如美国SRI International和剑桥大学的联合研究CHERI项目,为现有的芯片架构增加了新的功能;英国政府的数字安全设计(DSBD)计划汇集7000万英镑的政府资金和1.17亿英镑的工业联合投资开发技术;2022年,Arm公司探讨了其实验性Morello Program及CHERI架构实现原理,希望借此解决系统攻击中常被利用的一系列内存访问漏洞;而后,微软也参与了CHERI研发的相关工作。
即便厂商已经投入大量心力避免漏洞,但结果依然似乎不容乐观,这些也就是导致机构驳斥C/C++的根本原因,而这些机构则更推荐使用C#、Rust、Go、Java、Python和Swift等几个编程语言,尤其是Rust。
工程师,怎么看
那么用AI来暴力翻译成Rust可行吗?工程师是这样看的。
他们认为,唯一可行的方法是让翻译工具识别惯用的代码段,并在足够的抽象级别上将它们翻译成语言中适当的相关性。这只能在相当高层次的代码中真正工作,并且很多C和C++代码都在那个空间中。
因此,翻译过程可以侧重于识别如数组处理循环等关键模式,并自动替换为Rust中惯用的“安全”迭代器,从而有效避免因访问未定义内存区域等潜在问题导致的不安全行为。通过这种方式,可以显著减少在转换过程中引入的不安全因素。
如果你能够开发出将C语言转换为Rust语言的工具,那么在处理C代码中确实存在的内存错误时,会有两种情况发生:第一种情况,你尝试将这些错误一并复制过去,不过,这在理论上是不可能的,因为Rust中没有与C相同的错误构造;第二种情况,工具会捕获这些错误并发出警告。不过,上面两种情况似乎都不会发生,而更倾向于第三种情况:你复制了这些错误,当它们在运行时被触发时,你的程序会立即崩溃并安全退出,而不是变得可被利用。
如果能够将任意代码执行的情况转化为拒绝服务,那就将会是巨大的胜利。因为拒绝服务会让程序在漏洞处立即崩溃,而不是在漏洞之后的数万亿条指令之后才崩溃,这将使得问题更容易被定位和修复。毕竟,内存安全很多时候都是在运行时候检查出来的,你不可能神奇地坐着就看出来所有问题。
为了满足Rust借用检查器的要求而“修复”漏洞,有时需要在设计层面对程序数据结构进行更改。本质来说,这是在强制执行一种清晰的所有权模型,这种模型接近于高质量C/C++代码应该具备但往往并不具备的特性。
即使一个自动化工具能够识别出需要在这方面进行的更改(工程师对此持怀疑态度),那么将这些更改移植回C代码中,也只会让那些粗心的提交再次破坏内存所有权。Rust编译器将在未来继续强制执行这些规则(至少,我认为直到有人分叉编译器并引入“关闭借用检查,我只需要我的代码能编译”这样的便利功能,并使其变得流行起来为止,但这可能是不可避免的)。
当然,对于用AI翻译成Rust这件事儿,工程师持两方面态度,有人认为自己编程了三十多年,已经很久没遇到过内存漏洞这个问题,认为“软件行业一直在自掘坟墓”;另一方则认为这些人是“老顽固”,用AI翻译90%的编程语言,剩下的问题自己再手动解决,这样可以让系统在未来拥有更高的安全性,何乐而不为。
参考文献:
[1]The Register:https://www.theregister.com/2024/08/03/darpa_c_to_rust/
[2]CSDN:https://mp.weixin.qq.com/s/DYU13mXAcD9eurWHpxnyMw