技術(shù)編輯:MissD丨發(fā)自 思否編輯部
上周,Linux 內(nèi)核郵件列表上關(guān)于“社區(qū)最近討論了是否為內(nèi)核采用現(xiàn)代 C 語(yǔ)言標(biāo)準(zhǔn)”的信息引發(fā)業(yè)內(nèi)關(guān)注。剛剛,Linux 開源社區(qū)已正式宣布:內(nèi)核 C 語(yǔ)言版本將在未來(lái)升級(jí)到 C11,且預(yù)計(jì)將在今年 5 月份的 5.18 版本之后生效。

這個(gè)突然的決定,也終于讓擁有 30 年歷史的 Linux 內(nèi)核 C 語(yǔ)言迎來(lái)了升級(jí)。
眾所周知,想要說(shuō)服固執(zhí)的 Linux 之父 Linus Torvalds 絕非易事。那么,這一次 Linus Torvalds 為何終于松口了呢?這里面,似乎還真有那么一點(diǎn)偶然因素。
事件起因還是要回到上周的那次的 Linux 社區(qū)討論。
一條 Bug 引發(fā)的“連鎖反應(yīng)”
據(jù)悉,當(dāng)時(shí)一位名叫 Jakob Koschel 的博士生正在研究與內(nèi)核鏈表原語(yǔ)相關(guān)的推測(cè)性執(zhí)行漏洞,過(guò)程中他發(fā)現(xiàn)了一個(gè)問(wèn)題:Linux 內(nèi)核廣泛使用 struct list_head 定義的雙鏈表:struct?list_head?{
struct?list_head?*next,?*prev;
};
通常,開發(fā)者通過(guò)將此類結(jié)構(gòu)嵌入其他結(jié)構(gòu)里的方式,來(lái)使任何相關(guān)的結(jié)構(gòu)類型都可以創(chuàng)建鏈表。同時(shí),該內(nèi)核還提供了大量可用于遍歷和操作鏈表的函數(shù)和宏。其中一個(gè)就是 list_for_each_entry(),這是一個(gè)偽裝成控件結(jié)構(gòu)的宏。我們假設(shè)該內(nèi)核包含以下結(jié)構(gòu):struct?foo?{
int?fooness;
struct?list_head?list;
};
List 中的元素則可用于創(chuàng)建 foo 結(jié)構(gòu)的雙鏈接列表。假設(shè)有一個(gè)名為 foo_list 的結(jié)構(gòu)聲明作為此類鏈表的頭,則可以使用以下代碼遍歷此鏈表:struct?foo?*iterator;
list_for_each_entry(iterator,?&foo_list,?list)?{
do_something_with(iterator);
}
/?*Should?not?use?iterator?here*?/
list 參數(shù)告訴宏 foo 結(jié)構(gòu)中 list_head 結(jié)構(gòu)的名稱。對(duì)于迭代器指向的列表中的每個(gè)元素,該循環(huán)將執(zhí)行一次。而這樣就會(huì)導(dǎo)致 USB 子系統(tǒng)中出現(xiàn)錯(cuò)誤:在退出宏后,傳遞給該宏的迭代器仍可使用。當(dāng)然,這是一件非常“危險(xiǎn)”的事情。所以,Koschel 提交了一個(gè)補(bǔ)丁,重新編寫了有問(wèn)題的代碼,通過(guò)在循環(huán)結(jié)束后停止使用迭代器來(lái)修復(fù)這個(gè)錯(cuò)誤。隨后,Jakob Koschel 將(投機(jī)性安全列表迭代器建議)修復(fù)的與內(nèi)核鏈接表相關(guān)的預(yù)測(cè)執(zhí)行漏洞的補(bǔ)丁提交給了 Linus Torvalds。
最初,Linus Torvalds 本人似乎對(duì)這個(gè)補(bǔ)丁并不是很喜歡,也不知道該補(bǔ)丁與推測(cè)性執(zhí)行漏洞有什么關(guān)系。但經(jīng)過(guò) Koschel 詳細(xì)解釋之后,Linus 承認(rèn)了這只是一個(gè)常見的 Bug。然而,事情并非那么簡(jiǎn)單,Linus 很快就意識(shí)到了真正的問(wèn)題:傳遞給鏈表遍歷宏的迭代器必須在循環(huán)本身之外的范圍內(nèi)聲明。而出現(xiàn)這種不可預(yù)測(cè)的錯(cuò)誤的原因是 C89 中沒(méi)有“在循環(huán)中聲明變量”。我們知道,雖然 Linux 內(nèi)核正在快速發(fā)展,但它也依賴于一些非常古老的工具,其中之一就是其內(nèi)核代碼仍在使用 1989 年版的 C 語(yǔ)言標(biāo)準(zhǔn),也就是說(shuō),該標(biāo)準(zhǔn)是在內(nèi)核項(xiàng)目啟動(dòng) 30 多年前編寫的。像 list_for_each_entry()這樣的宏,基本上總是將最后一個(gè) HEAD 條目泄漏出循環(huán),就是因?yàn)椴荒茉谘h(huán)本身中聲明迭代器變量。如果可以編寫一個(gè)迭代器列表遍歷宏來(lái)聲明自己,那么迭代器在循環(huán)外就不可見,也不會(huì)出現(xiàn)這樣的問(wèn)題。然而,由于內(nèi)核停留在C89標(biāo)準(zhǔn)上,因此不可能在循環(huán)中聲明變量。因此,Linus 決定,“讓我們升級(jí)一下”,也許是時(shí)候升級(jí)到 C99 標(biāo)準(zhǔn)了,盡管 C99 也有 20 多年的歷史了,但它至少比 C89 更新一點(diǎn),且可以在循環(huán)中聲明變量。既然 C89 已經(jīng)過(guò)時(shí)了,為什么這么多年都沒(méi)有改變呢?Linus 解釋稱,“這是因?yàn)槲覀冊(cè)谝恍┡f的 gcc 編譯器版本上遇到了一些奇怪的問(wèn)題,這些版本不能隨意升級(jí)。”然而,現(xiàn)在 Linux 內(nèi)核已經(jīng)將 gcc 的最低要求提高到了 5.1 版,過(guò)去那些奇怪的 Bug 應(yīng)該消失了。另一位核心開發(fā)者 Arnd Bergmann 也對(duì)此事比較關(guān)注,他認(rèn)為可以升級(jí)到 C11 甚至更高版本,但升級(jí)到 C17 或 C2x 會(huì)破壞 gcc-5/6/7 支持,因此升級(jí)到 C11 更容易實(shí)現(xiàn)。最終,Linus Torvalds 支持了這個(gè)想法,并宣布將“在 5.18 版合并窗口的早期嘗試一下”。雖然接下來(lái)轉(zhuǎn)移到 C11 可能會(huì)導(dǎo)致一些意想不到的 Bug 也說(shuō)不定,但如果一切順利,下一個(gè) Linux 內(nèi)核版本將正式轉(zhuǎn)移到 C11。您對(duì)此次升級(jí)事件有何看法呢?也歡迎在下方交流互動(dòng)。想成為前端大佬?
測(cè)一測(cè)你的段位