自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux 內(nèi)核 API 的復(fù)雜歷史

系統(tǒng) Linux
由于用戶通常不會(huì)運(yùn)行最新的頂層 Linux 內(nèi)核,因此從用戶的角度來看,它也是一個(gè)非常分散的代碼庫,這對(duì)安全性有重要影響。

Linux 內(nèi)核是一個(gè)不斷迭代的代碼庫,截至 2021 年,其代碼行數(shù)已超過 3000 萬行。然而,由于用戶通常不會(huì)運(yùn)行最新的頂層 Linux 內(nèi)核,因此從用戶的角度來看,它也是一個(gè)非常分散的代碼庫,這對(duì)安全性有重要影響。一致的安全屬性(跨內(nèi)核版本、處理器架構(gòu)、編譯器版本)是grsecurity一個(gè)重要目標(biāo),但頂層通常不會(huì)將添加的預(yù)防性安全檢查或功能向后移植到舊支持的內(nèi)核,并且這些措施的狀態(tài)沒有很快被最終用戶采用,實(shí)際上,必須是有經(jīng)驗(yàn)的專注于安全的內(nèi)核開發(fā)人員才能確定他們自己系統(tǒng)的狀態(tài)。

作為系統(tǒng)管理員,我們首先要擔(dān)心的問題是一個(gè)系統(tǒng)的安全性和穩(wěn)定性,其次才是它的效能和效率。一個(gè)系統(tǒng)是否安全,一般來說與系統(tǒng)的復(fù)雜性有直接關(guān)系。對(duì)于必須開放訪問的系統(tǒng)來說,成熟而可靠的系統(tǒng)保護(hù)措施是十分重要的。而對(duì)作為各種程序運(yùn)行平臺(tái)的操作系統(tǒng)而言,系統(tǒng)越復(fù)雜可能隱藏的攻擊面也就越多。

受 copy_*_user 中的檢查影響的漏洞類型在過去已經(jīng)出現(xiàn)過幾次,例如, Mathias Krause 在 2013 年的修復(fù)報(bào)告。在討論這些檢查時(shí),最有意義的是談?wù)摦?dāng)前的頂層狀態(tài),之后我們可以討論代碼的歷史以及它是如何隨著時(shí)間的推移而演變的。

當(dāng)前頂層 copy_*_user() 檢查

在當(dāng)前的頂層內(nèi)核中,copy_from_user(用于將數(shù)據(jù)從用戶級(jí)復(fù)制到內(nèi)核)和copy_to_user(用于將數(shù)據(jù)從內(nèi)核復(fù)制到用戶級(jí))實(shí)現(xiàn)如下:

因此,如果 check_copy_size() 通過,則將調(diào)用較低級(jí)別的 copy_*_user 例程。這些較低級(jí)別的例程可以內(nèi)聯(lián)或不內(nèi)聯(lián),具體取決于處理器架構(gòu),但它們的實(shí)現(xiàn)方式保持不變:

access_ok() 是一個(gè)古老的例程,其目的是驗(yàn)證用戶空間中提供的范圍(作為 copy_from_user 的源,copy_to_user 的目標(biāo))是否實(shí)際駐留在用戶空間中。這個(gè) API 最近發(fā)生了變化,從 thread_info 結(jié)構(gòu)中刪除了 addr_limit 并將內(nèi)核拆分為內(nèi)核副本到它自己的 API。以前,在 addr_limit 恢復(fù)之前的 set_fs(KERNEL_DS) 或惡意修改的 addr_limit(過去幾年的常見漏洞利用技術(shù))之后運(yùn)行的代碼可以(ab)使用 copy*user 進(jìn)行任意內(nèi)核讀/寫。

should_fail_usercopy() 與錯(cuò)誤注入(模糊增強(qiáng))有關(guān),可以忽略。 instrument_* 同樣可以忽略,因?yàn)樗c KASAN(調(diào)試/模糊測(cè)試增強(qiáng))有關(guān)。出于生產(chǎn)安全目的,我們感興趣的唯一代碼是 access_ok() 和 check_copy_size()。

讓我們稍微解釋一下這里顯示的內(nèi)容:__compiletime_object_size()是一個(gè)宏,它利用了編譯器的__builtin_object_size()。如果內(nèi)核中用于復(fù)制的源或目標(biāo)(適當(dāng))的對(duì)象的大小能夠在編譯時(shí)確定,這將返回該對(duì)象的大小。否則,編譯器版本不支持__builtin_object_size(),它返回-1。

當(dāng)內(nèi)核對(duì)象的大小是靜態(tài)已知的并且要復(fù)制的長度也是恒定的時(shí),__bad_copy_from() 和 __bad_copy_to() 都是編譯時(shí)出現(xiàn)的錯(cuò)誤,這種情況在實(shí)踐中不太可能會(huì)變成安全問題,除非代碼從未測(cè)試/使用過。當(dāng)對(duì)象大小靜態(tài)已知但復(fù)制長度不是編譯時(shí)常量時(shí),Copy_overflow()是一個(gè)運(yùn)行時(shí)警告。

如果復(fù)制長度大于 INT_MAX,“WARN_ON_ONCE(bytes > INT_MAX)”檢查將產(chǎn)生運(yùn)行時(shí)警告。由于這是在無符號(hào) size_t 類型上計(jì)算的,因此當(dāng)在有符號(hào) int 或 long 類型上解釋時(shí)(即 oss-sec 上報(bào)告的錯(cuò)誤的情況),這具有拒絕負(fù)長度的附加效果。稍后會(huì)詳細(xì)介紹此檢查。

check_object_size() 來自頂層的USERCOPY 功能的有限版本。不會(huì)對(duì)其返回值進(jìn)行檢查,因?yàn)樗粫?huì)像其他檢查那樣簡單地使復(fù)制失敗,而是在 usercopy_abort() 中執(zhí)行 BUG() ,這在簡單的情況下將簡單地終止所涉及的進(jìn)程,但在更復(fù)雜的場(chǎng)景中,例如在用戶空間副本周圍保留互斥鎖可能會(huì)導(dǎo)致某些代碼路徑鎖定,或者在 panic_on_oops 場(chǎng)景中,會(huì)導(dǎo)致系統(tǒng)崩潰。

關(guān)于 oss-sec 報(bào)告中討論的漏洞的實(shí)際影響,由于 2018 年 6 月的"fsi: Add cfam char devices " 中針對(duì) Linux 4.19 引入了錯(cuò)誤的 CFAM 更改,因此 cfam_write() 案例將進(jìn)入 copy_from_user()使用負(fù)長度,到達(dá) check_copy_size() ,其中 __compiletime_object_size() 將返回 4 用于復(fù)制到的 __be32 數(shù)據(jù)變量,然后由于字節(jié)不是編譯時(shí)常量,將調(diào)用 copy_overflow(),觸發(fā)其中包含的WARN(),并錯(cuò)誤地中止復(fù)制操作。

為了簡潔起見,我們將把分析限制在這些方面,而不會(huì)深入raw_copy_*_user()本身的實(shí)現(xiàn),它已經(jīng)看到了自己的體系結(jié)構(gòu)特定的發(fā)展,包括SMAP/PAN/等的引入和Spectre的發(fā)現(xiàn)。

在繼續(xù)之前要注意的最后一點(diǎn)是memset()只存在于_copy_from_user()中,內(nèi)核注釋如下:

注意:1.只有 copy_from_user() 在短復(fù)制的情況下將目標(biāo)置零。2.__copy_from_user() 和 __copy_from_user_inatomic() 都不為零。3.他們的調(diào)用者絕對(duì)必須檢查返回值。

注意__copy_*_user(兩個(gè)下劃線)和_copy_*_user(一個(gè)下劃線)是不同的。這個(gè)memset()的原因大概是為了解決Linux歷史上copy_*_user被標(biāo)記為__must_check屬性之前的情況,要求調(diào)用者檢查返回值是否錯(cuò)誤??紤]一種常見的情況,即從用戶級(jí)復(fù)制結(jié)構(gòu),內(nèi)核更改了某些字段,然后將結(jié)構(gòu)復(fù)制回用戶級(jí)。如果來自用戶級(jí)的副本或內(nèi)核設(shè)置字段沒有寫入的區(qū)域被復(fù)制回用戶級(jí),且未初始化,它們可能會(huì)將之前的內(nèi)存內(nèi)容泄漏到用戶級(jí)(信息泄漏)。用戶級(jí)還可以通過使部分復(fù)制范圍包括未映射的地址或操作的無效權(quán)限來強(qiáng)制低級(jí)復(fù)制例程中的部分失敗。

check_object_size() 的歷史

這個(gè)檢查第一次出現(xiàn)在2016年6月的Linux 4.8版本中,通過commit“mm: Hardened usercopy”。它的提交信息如下:

“這是將PAX_USERCOPY移植到主線內(nèi)核的開始。這是由CONFIG_HARDENED_USERCOPY控制的第一組特性。這項(xiàng)工作是基于PaX Team和Brad Spengler的代碼,以及Casey Schaufler的早期移植。其他非平板頁面測(cè)試來自Rik van Riel。”

check_object_size() 通過引用堆和其他元數(shù)據(jù)來工作,以便在運(yùn)行時(shí)盡可能驗(yàn)證復(fù)制操作是否發(fā)生在單個(gè)對(duì)象的范圍內(nèi)。

盡管上面提到的提交消息是移植完整功能的開始,但在5年里,除了禁用上述添加的頁面測(cè)試(grsecurity中不存在)以外,該功能沒有出現(xiàn)其他重大變化,這破壞了內(nèi)核的幾個(gè)方面。PAX_USERCOPY最初發(fā)布于2009年,比有限的上游版本早了大約7年。強(qiáng)化后的用戶復(fù)制代碼沒有向后移植到早期版本,包括當(dāng)時(shí)的活動(dòng) LTS 版本。因此,頂層 4.4 XLTS。

__compiletime_object_size()/__builtin_object_size() 的歷史

__builtin_object_size()在2005年Arjan van de Ven編寫的FORTIFY_SOURCE補(bǔ)丁中首次使用。它最初關(guān)注的是FORTIFY_SOURCE在用戶級(jí)中也涵蓋的典型的str*和mem* api。2009 年,在研究 FORTIFY_SOURCE 在內(nèi)核中的實(shí)際覆蓋范圍時(shí),我將 Arjan 的工作擴(kuò)展到對(duì)更多函數(shù)執(zhí)行檢查,并增加了它對(duì)一些 [k|v]malloc 對(duì)象大小的編譯時(shí)知識(shí),發(fā)現(xiàn)它只檢測(cè)了約 30% 的涵蓋 API 實(shí)例,不過從安全角度來看,是不太可能出現(xiàn)被攻擊的。

這些 str* 和 mem* API 的覆蓋范圍是在 2017 年 7 月為 4.13 內(nèi)核提交“include/linux/string.h: add the option of fortified string.h functions ”,但沒有提及早期的工作,或者任何使用一些動(dòng)態(tài)分配對(duì)象大小的改進(jìn)知識(shí),將其有效覆蓋率降低到我最初調(diào)查的 30% 以下。

2009 年 Arjan van de Ven 通過提交“x86: Use __builtin_object_size() to validate the buffer size for copy_from_user()”為 x86 頂層添加了用于 copy__*_user 的 __builtin_object_size()。這個(gè)Linux 2.6.34的初始版本只覆蓋了copy_from_user,僅從 2013 年 10 月開始涵蓋 copy_to_user,其中 Jan Beulich 為 Linux 3.13 提交了"x86: Unify copy_to_user() and add size checking to it "。它看到了許多重構(gòu),最終通過 Al Viro 在 2017 年 3 月為 4.12 版本的 Linux 提交"generic ...copy_..._user primitives"產(chǎn)生了一個(gè)獨(dú)立于架構(gòu)的變體。

正如我們之前提到的,__builtin_object_size() 是由編譯器提供的。 2013 年 4 月,為了響應(yīng)內(nèi)核在某些 GCC 版本上現(xiàn)有使用內(nèi)置函數(shù)產(chǎn)生的編譯時(shí)錯(cuò)誤,Guenter Roeck 合并了“gcc4: disable __compiletime_object_size for GCC 4.6+”,使得整個(gè)練習(xí)對(duì)受影響的編譯器毫無用處版本(當(dāng)時(shí),最新發(fā)布的 GCC 版本是 4.8.0)。

“我想指出,盡管 __compiletime_object_size() 在 4.6 之前被限制為 gcc,但整個(gè)構(gòu)造將變得越來越?jīng)]有意義。 然而,我會(huì)質(zhì)疑 commit 2fb0815c9ee6b9ac50e15dd8360ec76d9fa46a2 ("gcc4: disable__compiletime_object_size for GCC 4.6+") 確實(shí)是必要的,相反,這應(yīng)該像從一開始就在這里做的那樣處理?!?/p>

然而,直到2016年8月Josh Poimboeuf的Linux 4.8 commit:“mm/usercopy: get rid of CONFIG_DEBUG_STRICT_USER_COPY_CHECKS”,對(duì)GCC >= 4.1和< 4.6使用__builtin_object_size()的限制被更改為GCC >= 4.1。在這次提交時(shí),GCC 6.2是最新的編譯器版本。Josh的提交也消除了啟用調(diào)試選項(xiàng)以獲得功能的需要。

綜上所述,在大約三年的時(shí)間里,除了少數(shù)內(nèi)核開發(fā)人員對(duì)內(nèi)核進(jìn)行檢查之外,用戶都沒有進(jìn)行過有關(guān)操作。還應(yīng)該注意的是,放寬對(duì) __builtin_object_size() 版本限制的 4.8 提交從未向后移植到早期的內(nèi)核,這意味著即使對(duì)于今天最新的 4.4 XLTS 版本,任何涉及此的檢查對(duì)于幾乎任何現(xiàn)代用戶都是完全無實(shí)操可能性的。

你可能想知道,Clang在構(gòu)建Linux時(shí)是如何發(fā)揮作用的,盡管谷歌在 2018 年底開始使用 Clang 構(gòu)建內(nèi)核的 4.4 內(nèi)核。 Clang 歷史上(甚至在最新版本中)偽造了 4.2.1 的 GCC 版本,因此不受這些更改的影響。

WARN_ON_ONCE 的歷史(bytes > INT_MAX)

這種檢查最早是 Linus Torvalds 本人在 2005 年 2 月對(duì)2.6.11 內(nèi)核通過BUG_ON() 對(duì) i386 進(jìn)行的檢查。由于Andi Kleen 的提交“[PATCH] i386: Remove copy_*_user BUG_ONs for (size < 0)”,兩年多后,這些檢查在2.6.22版本的內(nèi)核中被刪除,并錯(cuò)誤地解釋為“access_ok檢查這種情況,不需要檢查兩次”。這種解釋是錯(cuò)誤的,因?yàn)殡m然access_ok()確實(shí)有效地檢查了相同的情況,但BUG_ON()避免了后續(xù)memset()的執(zhí)行,此后memset()就可以執(zhí)行了。

在 grsecurity 中,2.6.29 版本的 Linux 在 2009 年 8 月時(shí)在 Linux 支持的幾乎所有架構(gòu)中安全地實(shí)施了此檢查,沒有 BUG_ON()。重要的是,此檢查是在 access_ok() 之前執(zhí)行的,并且在 copy_from_user() 的情況下避免了稍后將討論的易受攻擊的 memset()。

在頂層,這項(xiàng)檢查是由 Kees Cook 在 2019 年 12 月針對(duì) Linux 5.5通過“uaccess: disallow > INT_MAX copy sizes”引入的。由于只有 2 行更改,因此一個(gè)月后它被反向移植到 Linux 5.4。然而,由于 copy_*_user API 在前幾年經(jīng)歷了相當(dāng)大的變動(dòng),這個(gè)簡單的改變并沒有被進(jìn)一步的反向移植,因此在上游的4.4、4.9、4.14或4.19 XLTS內(nèi)核中也沒有出現(xiàn),而這些內(nèi)核現(xiàn)在仍然支持XLTS。

copy_from_user memset 的歷史

在 x86 上,至少可以追溯到 2002 年之前,只有在 access_ok() 成功時(shí)才對(duì)失敗的 copy_from_user 進(jìn)行歸零。例如當(dāng)copy_from_user的大小不是編譯時(shí)常量時(shí)調(diào)用 __generic_copy_from_user 的實(shí)現(xiàn)。后來,情況發(fā)生了變化,從 Linux 2.4.3.4 的這個(gè)提交開始,從隨后的2.4.35版本的這個(gè)改變開始,該版本在access_ok()失敗時(shí)添加了歸零設(shè)置。

Andrew Morton在2003年6月發(fā)布了一個(gè)x86補(bǔ)丁,提到了在access_ok()失敗的情況下重新放置memset()。

最有趣的是,早在2002年的Linux 2.4.4.4中,ARM就有以下變化:

目前尚不清楚該評(píng)論是指它緩解了前面描述的安全漏洞還是引入了一個(gè)漏洞,該漏洞可能由攻擊者控制長度的緩沖區(qū)歸零,然而,這一改變確實(shí)帶來了一個(gè)潛在漏洞??紤]到由于計(jì)算中的一些溢出而導(dǎo)致 n = -1 的情況:access_ok() 在 32 位 ARM 上將失敗,因?yàn)?n 表示為添加到任何用戶空間的無符號(hào)值地址將涵蓋包括內(nèi)核空間在內(nèi)的范圍。 else 情況將被觸發(fā),導(dǎo)致長度為 0xffffffff 的 memzero(),肯定會(huì)導(dǎo)致系統(tǒng)無法恢復(fù)的 DoS。

目前該問題仍然存在于頂層中。

由于2019年添加的禁止INT_MAX副本大小的頂層檢查是在check_copy_size()中實(shí)現(xiàn)的,因此它失敗了,避免了對(duì)_copy_from_user()的調(diào)用,這將執(zhí)行錯(cuò)誤的memset(),與十年前的grsecurity更改相同。然而,由于未知的原因,在提交消息或它所引用的郵件列表討論中,根本沒有提到修復(fù)這個(gè)漏洞。

如上所述,由于頂層的更改未向后移植到 4.4、4.9、4.14 或 4.19版本種,因此可以將負(fù)長度傳遞給 copy_from_user() 的內(nèi)核版本中存在的錯(cuò)誤可能會(huì)導(dǎo)致大量內(nèi)存損壞并保證系統(tǒng) DoS。如果有人接受某些人提出的緩解建議并啟用 panic_on_warn,2019年進(jìn)行的頂層更改也會(huì)通過恐慌導(dǎo)致 DoS。


責(zé)任編輯:武曉燕 來源: 嘶吼網(wǎng)
相關(guān)推薦

2014-05-13 15:00:59

2009-12-09 14:25:31

2009-12-21 16:01:07

2009-11-24 13:53:03

SuSE Linux

2013-11-07 13:59:56

Linux內(nèi)核

2013-11-25 14:07:11

Linux內(nèi)核內(nèi)核特性

2013-11-05 09:58:39

Linux內(nèi)核

2013-11-06 13:03:10

Linux內(nèi)核

2013-11-12 11:01:46

Linux內(nèi)核

2021-02-20 06:08:07

LinuxWindows內(nèi)核

2009-12-22 13:44:33

Linux操作系統(tǒng)

2012-02-06 09:49:40

2009-12-09 09:45:35

袁萌Linux

2011-01-11 13:45:06

2017-04-12 14:30:45

Linux內(nèi)核DebugFS

2013-05-13 09:52:52

Windows內(nèi)核Linux內(nèi)核

2012-04-01 14:44:49

Linux歷史

2010-03-02 09:17:32

Linux local

2022-01-24 17:08:33

Linux容器Docker

2010-06-02 16:46:31

Slackware
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)