[譯文] 空指針的樂趣(2)

[譯文] 空指針的樂趣(2)

作者:Jonathan Corbet
原文發佈日期:July 21, 2009
來源:http://lwn.net/Articles/342420/
譯者:王旭 ( http://wangxu.me , @gnawux ) http://wangxu.me/blog/?p=46
翻譯時間:2009年9月14日

譯者注:本來沒準備翻譯本系列的2,翻譯文章實際上是譯者的一種自我消遣方式,不過,翻譯了1之後,有很多朋友的反應很不錯,也有朋友在期待2,所以,就接着把2翻譯了,時間倉促,翻譯得不一定到位,請各位將就着看吧 :)

本系列的第一篇詳細分析了一長串的偶然錯誤,利用它們可以用一個空指針解引用來對內核進行攻擊。清除這個 bug 本身可以直接修改;實際上在這個問題的本質被大部分人所理解之前就已經修正了。從這個意義上講,這個問題本身相當小,很少有發佈版帶有有問題的內核版本。但是,這個攻擊證明了,內核中會有一大類類似的問題,在被修正之前,絕對有攻擊者有機會可以發現這些錯誤並利用它們。

一個顯而易見的問題是,當安全模塊機制被配置入內核的時候,管理員指定的最小合法用戶控件虛擬地址會被忽略,安全模塊允許覆蓋管理員指定的最小合法用戶空間地址限制((mmap_min_addr)。這個行爲顛覆了人們對安全模塊工作的理解:它們被認爲能夠限制權限而從不增加權限。而在這種情況下,SELinux 的存在實際是增加了權限,大部分部署的 SELinux 都沒能關閉這個漏洞(攻擊代碼的註釋中指出,AppAmor 也不會更好)。

此外,當完全不配置安全模塊的 時候,mmap_min_addr 也沒有被完全強制執行。目前主線(譯註:2.6.31)內核有一個補丁來保證 map_min_addr sysctl 設置永遠可用,這個補丁同時也被提交進入 2.6.27.27 和 2.6.30.2 更新了(以及其他的地方)。

問題同時也在SELinux層面進行了修復。Red Hat 未來的版本的 SELinux 將不再允許無限制的(在其他方面卻無特權的)進程將頁面映射到地址空間底端。雖然這裏仍然有一些未解決問題,特別是像WINE這樣的程序將陷入混亂。目前仍然不確定如何讓系統安全地支持一小撮需要映射到0頁面的程序。有個想法是讓 WINE 以 root 權限運行,這樣,或許太過類似 Windows 的行爲了,這些想法只收到很少的關注。

另一個關於 map_min_addr 的方法也必須要談到:一個帶有 SVR4 個性化的特權進程在 exec() 的時候,會有一個只讀頁面映射到0頁面。在十分罕見的情況下,一些老的 SVR4 程序會需要那裏有這麼一個頁面,但是,它實際讓空指針攻擊成爲了可能。於是,另一個被併入主線和穩定版更新中的補丁用來在 setuid 的程序運行的時候重置 SVR4 個性化(或者,至少重置那個關於映射0頁面的部分)。

這個改變對有些用戶仍然不夠,他們要求能夠完全關閉這個個性化功能。這個能力用於直接運行基於 386 的 Unix 的程序,其實已經沒有它 1995 年的時候所擁有的重要性了,所以,很多人質疑是否值得爲這個個性化特徵付出這樣的代價了。Linus 答道

我們可能可以扔掉那個愚蠢的特徵。它已經不再那麼重要了。有人真的關心麼?同時,這麼多年來,我們已經發展出了很多其他個性化標誌,其中有些仍然是有用的。

特別的,禁止地址空間隨機化(一個個性化特徵)的能力在很多情況下是很有用的。所以personality() 應該被留下來,不過,0頁映射的特徵可能要一去不返了。

這一連串錯誤中的一個就是被編譯器優化掉的空指針校驗。這個校驗可以阻止攻擊,但 GCC 卻把它優化掉了,因爲理論上說這個指針不應該是空的(因爲它已經被正常的解引用過了)。GCC(本身)有一個標誌可以禁止某種特殊優化;所以,從現在開始,內核將缺省使用 -fno-delete-null-pointer-checks 編譯開關。由於在內核中空指針的確可能是個合法的指針,無限地禁止這種優化史合理的。

有人可能會提出,雖然所有上面的改變都是好的,但可能部分地沒有切中要害:高質量的內核不應該在第一次發生的地方允許解引用空指針。這些解引用本身確實是 bug,這裏纔是最該修正的地方。這有些有趣的歷史,實際上內核開發者們常常建議忽略空指針校驗。比如,如下 BUG_ON() 代碼:

    BUG_ON(some_pointer == NULL);
    /* dereference some_pointer */

經常被以這樣的註釋而刪除掉:

如果我們解引用 NULL,那麼內核將基本上會和 BUG 一樣顯示一個信息,也會採取相同的措施。所以添加一個 BUG_ON 實際無法得到任何收益。

這是因爲,解引用空指針會導致一個內核 oops。表面上,這是合理的:如果我們的硬件可以檢測到空指針解引用,那麼添加軟件開銷來檢測它沒什麼意義。但這個原因實際上不是無懈可擊的,這次的攻擊代碼就說明了這一點。映射到0頁確實是有一定原因的,所以,一個空指針未必總是非法的。你可能會假設所有相關開發者都理解這一點,但實際上內核中仍然有很多地方確實有游泳的空指針檢驗代碼被刪除掉了。

雖然如此,大部分內核中的空指針問題可能僅僅就是失察造成的。如果沒有方法在相關代碼中實際使用空指針的話,大部分的空指針問題是不可被利用的,缺少檢驗並不能帶來什麼。不過,如果能把它們全都修復肯定是件好事。

發現這些問題可能可以通過 Smatch 靜態代碼分析工具。Smatch悄無聲息很多年了,不過 Dan Carpenter 正在讓它死灰復燃;他最近發佈了 Smatch 爲他發現的一個空指針 bug。如果 Smatch 能成爲一個通用的發現此類錯誤的工具的話,內核將會更加安全。但不幸的是,這個檢查器似乎沒有吸引開發者們的很多興趣,自由軟件依舊落後這個領域中的先進技術很遠,這讓我們都很受傷。

另一個方法由 Kulia Lawall 所採用,她使用一個 Coccinelle “語義補丁”來發現並修復 TUN 驅動中發現的這種先解引用再檢查的 bug。一系列補丁()被髮布出來,用於修復一系列類似 bug。指針在檢查之前被解引用可能只是內核中的空指針問題的一個子集,但每個都被程序員們認爲有可能出現空指針,並且是有問題的。所以它們顯然是應該被修復的。

總之,這個攻擊代碼可以看做是內核社區的一個起牀號,十分幸運,它將幫助清理很多代碼,並消滅很多安全問題。攻擊代碼的作者 Brad Spengler 還明確的希望得到更多:他經常會發布一些對於內核安全問題的關切:嚴重的內核安全 bug 被悄悄地修復,或在最差情況下被變爲拒絕服務的問題。這個問題是否會改變現狀呢,在內核環境中,很多 bug 會造成安全隱患,這些在 bug 被修復的時候並不會立刻顯現出來。所以我們無法看到更多 bug 被以安全問題的方式被髮布出來,不過幸運的是,我們將會看到更多的 bug 被修復。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章