Read-onlydynamicdata
lwn文章翻譯,原文連結
簡介
本文主要講述的是一種動態記憶體的只讀保護機制。
原文
核心開發者可以對想保護的資料設定為read-only許可權,藉助於MMU來避免惡意攻擊者的篡改。kernel目前已經支援只讀記憶體保護,但這些記憶體必須在作業系統自舉完成前被初始化,所以侷限性很大。Igor Stoppa的一組patch彌補了這篇空白,他提出一組新API。
已有的只讀保護機制,最直觀當屬const修復符,但它是編譯時檢查。post-init read-only data mechanism是kernel目前已支援的只讀保護機制,它來自於grsecurity patch,所有的資料必須在作業系統引導階段完成初始化,在此之後則不允許修改。那麼作業系統初始化後,如果想保護動態申請的記憶體該怎麼辦呢?目前kernel並無此類機制,Igor Stoppa則提出了”protectable memory allocator”(簡稱pmalloc),核心思想是建立一個pool,所有的只讀物件從pool中分配。API如下:
- 建立pool物件,返回pool控制程式碼:
#include <linux/pmalloc.h>
struct pmalloc_pool *pool = pmalloc_create_pool();
- 從pool中申請記憶體,返回記憶體地址,此時可讀寫:
void *pmalloc(struct pmalloc_pool *pool, size_t size);
void *pzalloc(struct pmalloc_pool *pool, size_t size);
void *pmalloc_array(struct pmalloc_pool *pool, size_t n, size_t size);
void *pcalloc(struct pmalloc_pool *pool, size_t n, size_t size);
char *pstrdup(struct pmalloc_pool *pool, const char *s);
上述API與使用者態的libc介面很像,其基礎介面是pmalloc(),其他都是一些變種。
- 使用者對返回的地址進行初始化,然後設定read-only許可權,返回後則不能再進行修改:
void pmalloc_protect_pool(struct pmalloc_pool *pool);
呼叫此函式之後,使用者可以接著申請記憶體,即此API修改的是已申請記憶體相對應的頁表許可權。由於頁表許可權都是以4k為單位,所以此處有可能造成記憶體浪費。
- 銷燬:
void pmalloc_destroy_pool(struct pmalloc_pool *pool);
(譯者注:pmalloc並未提供free()介面,所以作為allocater而言,其實現是比較簡單的。當然,它的目的是安全,而不是記憶體分配。)
pmalloc()是基於vmalloc(),所以不能在原子上下文中使用。需要注意的是,pmalloc是通過修改vmalloc()申請的記憶體地址頁表許可權,所以攻擊者完全可以繞過此處的地址,而直接通過system memory map來篡改資料。(譯者注:有點像棧上的const區域性變數,可以通過指標直接修改。)
最後,pmalloc這一組patch還缺乏明確的使用案例,社群也不打算直接merge此類缺少use case的patch,後續待觀望。
譯者總結
純從安全形度考慮,pmalloc無法做到絕對安全。如果攻擊者已經取得核心態的可寫許可權,那麼可以直接修改頁表項獲得pmalloc申請物件的可寫許可權。譯者覺得pmalloc機制更適合用於發現kernel自身的程式碼bug,保留第一案發現場,這一點對於核心開發除錯具有很重要的意義。pmalloc不支援free,這一點會限制其使用場景;不過若需支援free,allocator邏輯的複雜度就上去了,有點小題大做。pmalloc還需要更多的use case來證明其價值。