首頁 > 收藏

Linux之父終於被勸動:用了30年的Linux核心C語言將升級至C11

作者:由 量子位 發表于 收藏日期:2022-12-31

linux核心是誰寫的

曉查 發自 凹非寺

量子位 | 公眾號 QbitAI

還在使用89年版C語言的

Linux核心

,現在終於要做出改變了。

今天,Linux開源社群宣佈,未來會把核心C語言版本升級到

C11

,預計

5.18

版之後生效,也就是今年5月。

Linux之父終於被勸動:用了30年的Linux核心C語言將升級至C11

這個決定很突然,從發起問題到官方宣告,不過才一個星期,要知道說服固執的Linux之父

Linus Torvalds

可不是件容易的事。

事情的原因,說起來還有那麼一點偶然的因素。

一個bug的連鎖反應

問題的起源是來自上週的一次Linux社群討論。

一位名叫

Jakob Koschel

的博士生,在研究阻止與核心連結串列primitive相關的預測執行漏洞時,發現了這樣一個問題。

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子系統中的一個bug:

傳遞給該宏的迭代器在退出宏後還能被使用。

這是一件危險的事情,所以Koschel提交了一個修復補丁,在迴圈後停止使用迭代器搞定了bug。

Linux之父終於被勸動:用了30年的Linux核心C語言將升級至C11

說服Linus

但是Linus Torvalds本人並不太喜歡這個補丁,也沒有看到它與預測執行漏洞的關係。在Koschel詳細解釋後,Linus承認這只是一個普通的bug。

然而事情並沒有那麼簡單,Linus不久後意識到了真正的根源:

傳遞給連結串列遍歷宏的迭代器,必須在迴圈本身之外的範圍內宣告。

這種非預測性bug發生的原因是,C89中沒有“在迴圈中宣告變數”。

像list_for_each_entry()這樣的宏,從根本上總是將最後一個HEAD入口洩漏到迴圈之外,僅僅是因為我們不能在迴圈本身中宣告迭代器變數。

如果可以編寫一個可以宣告自己的迭代器列表遍歷宏,那麼迭代器在迴圈之外將不可見,並且不會出現此類問題。

但是,由於核心停留在C89標準上,因此無法在迴圈中宣告變數。

Linus決定,那咱們還是升級吧,也許是時候轉向C99標準了。

雖然它也有20多年的歷史,但至少比C89新,可以在迴圈中宣告變數。

既然C89如此陳舊,這麼多年還沒做出改變呢?Linus說,那是因為我們在一些古老的gcc編譯器版本中遇到了一些奇怪的問題,不能隨便升級。

Linux之父終於被勸動:用了30年的Linux核心C語言將升級至C11

但是,現在Linux核心已將gcc的最低要求提升至5。1版,因此過去那些奇怪的bug應該不會有了。

而另一位核心開發者Arnd Bergmann認為,咱們完全可以升級到C11甚至更高版本。但如果升級到C17或C2x,會破壞對gcc-5/6/7的支援,因此升級到C11更容易實現。

最終,Torvalds贊成這個想法:“好的,請提醒我,讓我們在5。18合併視窗的早期嘗試一下。”

接下來遷移到C11可能會導致一些意想不到的bug,但如果一切順利,下一個Linus核心版本將正式轉向C11。

參考連結:

[1]https://lwn。net/SubscriberLink/885941/01fdc39df2ecc25f/

[2]https://news。ycombinator。com/item?id=30459634