终于!“30 岁”的 Linux 内核 C 语言将升级到 C11

简介: 终于!“30 岁”的 Linux 内核 C 语言将升级到 C11

上周,Linux 内核邮件列表上关于“社区最近讨论了是否为内核采用现代 C 语言标准”的信息引发业内关注。刚刚,Linux 开源社区已正式宣布:内核 C 语言版本将在未来升级到 C11,且预计将在今年 5 月份的 5.18 版本之后生效。

image.png

这个突然的决定,也终于让拥有 30 年历史的 Linux 内核 C 语言迎来了升级。

众所周知,想要说服固执的 Linux 之父 Linus Torvalds 绝非易事。那么,这一次 Linus Torvalds 为何终于松口了呢?这里面,似乎还真有那么一点偶然因素。

事件起因还是要回到上周的那次的 Linux 社区讨论。

一条 Bug 引发的“连锁反应”

据悉,当时一位名叫 Jakob Koschel 的博士生正在研究与内核链表原语相关的推测性执行漏洞,过程中他发现了一个问题:Linux 内核广泛使用 struct list_head 定义的双链表:

image.png

struct list_head {

structlist_head *next, *prev;

};

通常,开发者通过将此类结构嵌入其他结构里的方式,来使任何相关的结构类型都可以创建链表。同时,该内核还提供了大量可用于遍历和操作链表的函数和宏。其中一个就是 list_for_each_entry(),这是一个伪装成控件结构的宏。

恰巧,问题出在了这个宏上。

我们假设该内核包含以下结构:

image.png

struct foo {

int fooness;

struct list_head list;

};

List 中的元素则可用于创建 foo 结构的双链接列表。

假设有一个名为 foo_list 的结构声明作为此类链表的头,则可以使用以下代码遍历此链表:

image.png

struct foo *iterator;

list_for_each_entry(iterator, &foo_list, list) {

do_something_with(iterator);

}

/ Should not use iterator here /

list 参数告诉宏 foo 结构中 list_head 结构的名称。对于迭代器指向的列表中的每个元素,该循环将执行一次。

而这样就会导致 USB 子系统中出现错误:在退出宏后,传递给该宏的迭代器仍可使用。当然,这是一件非常“危险”的事情。

image.png

所以,Koschel 提交了一个补丁,重新编写了有问题的代码,通过在循环结束后停止使用迭代器来修复这个错误。随后,Jakob Koschel 将(投机性安全列表迭代器建议)修复的与内核链接表相关的预测执行漏洞的补丁提交给了 Linus Torvalds。

Linux 之父终于被说服

最初,Linus Torvalds 本人似乎对这个补丁并不是很喜欢,也不知道该补丁与推测性执行漏洞有什么关系。但经过 Koschel 详细解释之后,Linus 承认了这只是一个常见的 Bug。

然而,事情并非那么简单,Linus 很快就意识到了真正的问题:传递给链表遍历宏的迭代器必须在循环本身之外的范围内声明。

而出现这种不可预测的错误的原因是 C89 中没有“在循环中声明变量”。

image.png

我们知道,虽然 Linux 内核正在快速发展,但它也依赖于一些非常古老的工具,其中之一就是其内核代码仍在使用 1989 年版的 C 语言标准,也就是说,该标准是在内核项目启动 30 多年前编写的。

像 list_for_each_entry()这样的宏,基本上总是将最后一个 HEAD 条目泄漏出循环,就是因为不能在循环本身中声明迭代器变量。

如果可以编写一个迭代器列表遍历宏来声明自己,那么迭代器在循环外就不可见,也不会出现这样的问题。

然而,由于内核停留在C89标准上,因此不可能在循环中声明变量。

因此,Linus 决定,“让我们升级一下”,也许是时候升级到 C99 标准了,尽管 C99 也有 20 多年的历史了,但它至少比 C89 更新一点,且可以在循环中声明变量。

既然 C89 已经过时了,为什么这么多年都没有改变呢?Linus 解释称,“这是因为我们在一些旧的 gcc 编译器版本上遇到了一些奇怪的问题,这些版本不能随意升级。”

然而,现在 Linux 内核已经将 gcc 的最低要求提高到了 5.1 版,过去那些奇怪的 Bug 应该消失了。

另一位核心开发者 Arnd Bergmann 也对此事比较关注,他认为可以升级到 C11 甚至更高版本,但升级到 C17 或 C2x 会破坏 gcc-5/6/7 支持,因此升级到 C11 更容易实现。

最终,Linus Torvalds 支持了这个想法,并宣布将“在 5.18 版合并窗口的早期尝试一下”。

虽然接下来转移到 C11 可能会导致一些意想不到的 Bug 也说不定,但如果一切顺利,下一个 Linux 内核版本将正式转移到 C11。您对此次升级事件有何看法呢?也欢迎在下方交流互动。

相关文章
|
4月前
|
安全 网络协议 Linux
深入理解Linux内核模块:加载机制、参数传递与实战开发
本文深入解析了Linux内核模块的加载机制、参数传递方式及实战开发技巧。内容涵盖模块基础概念、加载与卸载流程、生命周期管理、参数配置方法,并通过“Hello World”模块和字符设备驱动实例,带领读者逐步掌握模块开发技能。同时,介绍了调试手段、常见问题排查、开发规范及高级特性,如内核线程、模块间通信与性能优化策略。适合希望深入理解Linux内核机制、提升系统编程能力的技术人员阅读与实践。
456 1
|
4月前
|
Ubuntu Linux
Ubuntu 23.04 用上 Linux 6.2 内核,预计下放到 22.04 LTS 版本
Linux 6.2 带来了多项内容更新,修复了 AMD 锐龙处理器设备在启用 fTPM 后的运行卡顿问题,还增强了文件系统。
|
4月前
|
Ubuntu Linux
Ubuntu 23.10 现在由Linux内核6.3提供支持
如果你想在你的个人电脑上测试一下Ubuntu 23.10的最新开发快照,你可以从官方下载服务器下载最新的每日构建ISO。然而,请记住,这是一个预发布版本,所以不要在生产机器上使用或安装它。
|
4月前
|
监控 Ubuntu Linux
什么Linux,Linux内核及Linux操作系统
上面只是简单的介绍了一下Linux操作系统的几个核心组件,其实Linux的整体架构要复杂的多。单纯从Linux内核的角度,它要管理CPU、内存、网卡、硬盘和输入输出等设备,因此内核本身分为进程调度,内存管理,虚拟文件系统,网络接口等4个核心子系统。
350 0
|
4月前
|
Web App开发 缓存 Rust
|
4月前
|
Ubuntu 安全 Linux
Ubuntu 发行版更新 Linux 内核,修复 17 个安全漏洞
本地攻击者可以利用上述漏洞,攻击 Ubuntu 22.10、Ubuntu 22.04、Ubuntu 20.04 LTS 发行版,导致拒绝服务(系统崩溃)或执行任意代码。
|
3月前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
1030 0
|
11月前
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
694 23
|
5月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
334 15