开发者生态
evening
Rust 和 C/C++ 之间的内存安全 CVE 有所不同
2026-06-15
1 阅读
nicoburns
CVE 是一个用于对软件中的安全漏洞进行分类和报告的数据库。可以报告的漏洞有多种。其中一些只是由程序逻辑中的错误引起的(例如 Cargo 最近报告的 CVE),但一些最令人讨厌的错误是由内存不安全引起的,这很容易导致漏洞利用。在这篇文章中,我想重点讨论后一种 CVE、它们的报告方式(尤其是在库中),以及 Rust 与 C 或 C++ 之间的区别。因为有时我在网上看到人们比较 Rust 和 C/C++ 软件中的 CVE 数量,这往往伴随着这样的说法:Rust 并不是真正的内存安全,或者当 CVE 仍然存在时不值得采用。有时,当我向习惯使用 C 或 C++ 编程的程序员教授 Rust 时,我也会观察到类似的观点。当然,现在任何人都可以自由地进行此类比较,并据此得出自己的结论。但我认为 Rust 和 C/C++ 中处理与内存安全相关的潜在漏洞的方式存在重要差异,一开始可能并不明显,特别是如果您不知道 Rust 是如何工作的。我想在这篇文章中解释这一点。但首先,我应该澄清,Rust 中绝对有可能导致内存不安全错误和未定义行为。在绝大多数情况下 1 ,需要 unsafe 关键字才能发生这种情况,但任何声称 Rust 程序根本无法体验 UB 的人都是错误的。 Rust 也完全有可能导致一般漏洞(即与内存不安全无关的漏洞)。毕竟,忘记添加检查以确保您的管理仪表板只能由管理员访问在任何语言中都可能发生。然而,Rust 和 C 或 C++ 中的潜在漏洞之间存在着很大的不同,这与 Rust 在实践中实际上比 C 或 C++ 内存安全得多的核心原因有关。我将尝试在用 C 语言编写的curl 网络库上演示它。curl 中的潜在漏洞? (lib)curl 是世界上使用最多且维护良好的开源库之一。它的主要开发人员 Daniel Stenberg 是我们这个时代最多产的开源维护者之一,在过去 30 年里,他与许多其他人一起努力改进这个库。尽管最近不得不处理法学硕士发现的大量 CVE,但他和他的合作者在保护 curl 免受潜在攻击和漏洞方面做得非常好,并且他们为curl 是一款非常强大的软件而感到自豪。那么,让我们来测试一下,好吗?我打开 libcurl 的文档,找到了我看到的第一个接受参数的函数,curl_getenv。这应该是一个简单的函数,它提供了一个可移植的抽象,用于跨不同操作系统获取环境变量的值。 curl 应该是安全和健壮的,所以这个函数肯定不包含任何 UB 或内存不安全,对吗?那么下面的C程序呢? #include int main ( void ) { curl_getenv ( NULL );这个 5 行 C 程序非常简单,它只是使用 NULL 指针参数调用curl_getenv 函数,并且编译时没有任何警告。然而,当你执行它时,你(可能)会遇到段错误,从而出现内存安全错误,从而出现潜在的漏洞/利用: $ gcc test.c -otest -lcurl -Wall -Wextra $ ./test Segmentation failure ( core dumped ) 当然,这个程序人为地简单,但这就是重点。实际上,在较大的程序中,这样的情况很容易(而且确实)经常发生。呵呵。那么也许curl 并不是那么安全?我应该去报告这是curl 中的漏洞吗?!不,当然不是。那太愚蠢了。我知道,你也知道。但我们如何真正知道它呢?这是有趣的部分。考虑一个非常相似的程序,它会调用如下函数:curl_getenv("FOO")。如果该程序仍然存在段错误,从而包含潜在的漏洞怎么办?我确信卷曲维护者想知道这种情况的发生,并且如果我报告它,他们会认为这是一个相当大的问题!同时,我确信如果我将第一个程序报告为curl 中的漏洞,他们会(理所当然地)告诉我。然而这两个计划的差别很小。那么,什么给出呢?好吧,在实践中,像我原来的例子中那样的 UB 据说是由“错误使用”2 引起的,并且它不被认为是我正在使用的库或 API 中的问题,而是在我的(应用程序)代码中。这样做主要是出于以下两个原因:在 C 中,由于其有限的类型系统,通常不可能精确地指定 API 的约定(不变量、前置条件、后置条件等)3,并且库作者通常不会费心描述所有可能的错误用法,因为这不切实际