当前位置: 首页 > 科技观察

Linus终于被说服:30年的Linux内核C语言要升级了

时间:2023-03-13 12:07:30 科技观察

虽然Linux内核发展迅速,但也依赖于一些非常古老的工具,其中之一就是内核代码仍在使用1989版C语言标准C——这个标准是在30多年前内核项目开始之前编写的。从讨论的结果来看,这种情况有望在5.18内核中得到改变。JakobKoschel在LinusTorvalds(https://lkml.org/lkml/2022/2/17/1032)的补丁中修复了与内核链表相关的推测执行漏洞。原因是雅各布发现了一个问题。Linux内核广泛使用由structlist_head定义的双向链表:structlist_head{structlist_head*next,*prev;};这种结构通常嵌入在其他结构中,这样,开发人员可以使用任何感兴趣的结构类型来制作链表。此外,内核还提供了大量的函数和宏,可以用来遍历和操作链表。其中之一是list_for_each_entry(),这是一个伪装成控制结构的宏。要查看这个宏是如何使用的,假设内核包含以下结构:structfoo{intfooness;结构list_head列表;};list成员可用于创建foo结构的双向链表,假设我们有一个名为foo_list的结构声明为此类链表的头部,可以使用以下代码遍历:structfoo*iterator;list_for_each_entry(iterator,&foo_list,list){do_something_with(iterator);}/*此处不应该使用iterator*/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。