本帖最后由 Killoser 于 2022-3-1 15:24 编辑

Linux 内核在快速发展,但它同时依赖于一些相当古老的工具,其中之一是代码仍然采用的是 1989 版的 C 语言标准,已有三十多年历史了。

就在上周,Linux 开源社区正式宣布:内核 C 语言版本将在未来升级到 C11,且预计将在今年 5 月份的 5.18 版本之后生效。

事情起源是来自2月的一次 Linux 社区讨论。

一个bug的连锁反应,从发起问题到官方声明,不过才一个星期:

一位名叫 Jakob Koschel 的博士生正在研究与内核链表原语相关的推测性执行漏洞,过程中他发现了一个问题:Linux 内核广泛使用 struct list_head 定义的双链表:
struct list_head {
  •     struct list_head *next, *prev;
  •     };
  • 复制代码
    通常,开发者通过将此类结构嵌入其他结构里的方式,来使任何相关的结构类型都可以创建链表。同时,该内核还提供了大量可用于遍历和操作链表的函数和宏。其中一个就是 list_for_each_entry(),这是一个伪装成控件结构的宏。

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

    我们假设该内核包含以下结构:
    struct foo {
  •         int fooness;
  •     struct list_head list;
  •     };
  • 复制代码
    List 中的元素则可用于创建 foo 结构的双链接列表。

    假设有一个名为 foo_list 的结构声明作为此类链表的头,则可以使用以下代码遍历此链表:
    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 子系统中出现错误:在退出宏后,传递给该宏的迭代器仍可使用。当然,这是一件非常“危险”的事情。

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

    这位名叫 Jakob Koschel 的博士生 向 Linus Torvalds 递交的补丁引发了采用现代 C 语言标准的讨论。他的补丁旨在修复内核链表相关的预测执行漏洞。Linus Torvalds 对补丁修复的问题表示不解, Jakob Koschel对此做出了进一步的解释,Linus Torvalds 随后认为也许可以采用更直接的修复如块级变量声明。但 C89 不支持,而 1999 年发布的 C99 标准支持。Linus Torvalds 说,内核代码一直停留在 C89 的原因之一是编译器 gcc 的旧版本会出现奇怪的问题,现在内核要求的 gcc 最低版本已经提高到了 v5.1 那些 bug 可能不再相关了。另一位内核开发者 Arnd Bergmann 提议直接升级到 C11 甚至 C2x,但跨越太大内核社区未必接受。Torvalds 宣布将在下一个内核版本 v5.18 中尝试下。如果一切顺利,C 语言标准有望在下一个内核版本中迁移到 C11。

    来源:SegmentFault思否,Solidot