Read-onlydynamicdata

alidenggang發表於2018-09-12

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來證明其價值。