終於!“30 歲”的 Linux 核心 C 語言將升級到 C11

MissD發表於2022-03-01

上週,Linux 核心郵件列表上關於“社群最近討論了是否為核心採用現代 C 語言標準”的資訊引發業內關注。剛剛,Linux 開源社群已正式宣佈:核心 C 語言版本將在未來升級到 C11,且預計將在今年 5 月份的 5.18 版本之後生效。

這個突然的決定,也終於讓擁有 30 年曆史的 Linux 核心 C 語言迎來了升級。

眾所周知,想要說服固執的 Linux 之父 Linus Torvalds 絕非易事。那麼,這一次 Linus Torvalds 為何終於鬆口了呢?這裡面,似乎還真有那麼一點偶然因素。

事件起因還是要回到上週的那次的 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。

Linux 之父終於被說服

最初,Linus Torvalds 本人似乎對這個補丁並不是很喜歡,也不知道該補丁與推測性執行漏洞有什麼關係。但經過 Koschel 詳細解釋之後,Linus 承認了這只是一個常見的 Bug。

然而,事情並非那麼簡單,Linus 很快就意識到了真正的問題:傳遞給連結串列遍歷巨集的迭代器必須在迴圈本身之外的範圍內宣告。

而出現這種不可預測的錯誤的原因是 C89 中沒有“在迴圈中宣告變數”。

我們知道,雖然 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。您對此次升級事件有何看法呢?也歡迎在下方交流互動。

相關文章