根据Linux内核邮件列表的消息,社区最近讨论是否要对内核采用现代C语言标准。Linux内核在快速成长的同时,也依赖于一些非常古老的工具,其中之一就是内核代码仍然使用1989版的C语言标准——编写于30多年前内核项目开始之前。从讨论的结果来看,这种情况有望在5.18版本的内核中得到改变。JakobKoschel在提交给LinusTorvalds(https://lkml.org/lkml/2022/2/…)的补丁中修复了与内核链表相关的推测执行漏洞。Linux内核C语言将升级Linux内核C语言升级的原因是Jakob发现了一个问题。Linux内核广泛使用由structlist_head定义的双向链表:structlist_head{structlist_headnext,prev;};这种结构通常嵌入到其他结构中,这样开发者就可以使用他们感兴趣的任何结构类型来制作链表。此外,内核还提供了大量的函数和宏,可以用来遍历和操作链表.其中之一是list_for_each_entry(),这是一个伪装成控制结构的宏。要查看这个宏是如何使用的,假设内核包含以下结构:structfoo{intfooness;structlist_headlist;};列表成员可用于创建foo结构的双向链表,假设我们有一个名为foo_list的结构声明作为此类链表的头部,可以使用以下方法迭代此列表:structfoo*iterator;list_for_each_entry(迭代器,&foo_list,列表){do_something_with(迭代器);}/这里不应该使用迭代器/list参数告诉宏foo结构中list_head结构的名称。该循环将对迭代器指向的列表中的每个元素执行一次。这导致了USB子系统中的一个错误:传递给这个宏的迭代器可以在退出宏后使用。Koschel通过重写违规代码以在循环后停止使用迭代器解决了这个问题。然而,Linus对打补丁的问题一头雾水,并没有看出它与推测执行漏洞的关系。Koschel进一步解释了这一点,Linus认为这只是一个普通的错误。但没过多久Linus就发现了问题的根源:传递给列表遍历宏的迭代器必须在循环本身之外的范围内声明。后来,Linus认为也许可以采用更直接的修复方法,例如块级变量声明。但是C89不支持,1999年发布的C99标准支持。所以也许是时候让Linux内核转向C99标准了。Linus表示,内核代码一直卡在C89的原因之一是老版本的编译器gcc会出现奇怪的问题,导致initializer被破坏。但现在内核要求的最低GCC版本已经提升到v5.1,那些错误可能不再相关。另一位关注架构编译器问题的内核开发人员ArndBergmann建议直接升级到C11甚至C2x,尽管他不确定C11是否会给内核带来任何新东西。但是如果升级到C17或者C2x,会破坏对gcc-5/6/7的支持,所以升级到C11更容易实现,跨度太大内核社区可能不会接受。Linus接受了这个想法,在Bergmann确认应该可以这样做之后,Linus宣布他将在下一个内核版本v5.18中尝试使用C11标准。如果一切顺利,下一个内核版本使用的C语言标准有望升级为C11。
