原子替換 & 累積補丁

Livepatch 之間可能存在依賴關係。如果多個補丁需要對同一函式進行不同的更改,那麼我們需要定義安裝補丁的順序。任何較新的 livepatch 中的函式實現都必須建立在較舊的補丁之上。

這可能會成為一個維護噩夢,尤其是當更多補丁以不同方式修改同一函式時。

一個優雅的解決方案是“原子替換”功能。它允許建立所謂的“累積補丁”。它們包括所有較舊的 livepatch 中所有需要的更改,並在一次轉換中完全替換它們。

用法

可以透過在 struct klp_patch 中設定 "replace" 標誌來啟用原子替換,例如

static struct klp_patch patch = {
        .mod = THIS_MODULE,
        .objs = objs,
        .replace = true,
};

然後,所有程序都遷移到僅使用來自新補丁的程式碼。一旦轉換完成,所有舊補丁將自動停用。

Ftrace 處理程式將透明地從不再被新的累積補丁修改的函式中刪除。

因此,livepatch 作者可能只需要維護一個累積補丁的原始碼。這有助於在新增或刪除各種修復或功能時保持補丁的一致性。

在轉換完成後,使用者可以僅保留系統上安裝的最後一個補丁。這有助於清楚地瞭解實際使用的程式碼。此外,livepatch 可能會被視為一個修改核心行為的“正常”模組。唯一的區別是它可以在執行時更新而不會破壞其功能。

特性

原子替換允許

  • 在升級其他函式的同時,原子地還原先前補丁中的某些函式。

  • 消除不再修補的函式的核心重定向可能造成的效能影響。

  • 減少使用者對 livepatch 之間依賴關係的困惑。

限制:

  • 一旦操作完成,就沒有簡單的方法來反轉它並原子地恢復被替換的補丁。

    一個好的做法是在任何釋出的 livepatch 中設定 .replace 標誌。然後,重新新增較舊的 livepatch 相當於降級到該補丁。只要 livepatch 在(取消)補丁回撥或 module_init()module_exit() 函式中沒有進行額外的修改,這樣做是安全的,如下所述。

    另請注意,僅當轉換未被強制執行時,才可以刪除並重新載入被替換的補丁。

  • 僅執行 _新_ 累積 livepatch 中的(取消)補丁回撥。來自被替換補丁的任何回撥都將被忽略。

    換句話說,累積補丁負責執行正確替換任何舊補丁所需的任何操作。

    因此,用較舊的補丁替換較新的累積補丁可能很危險。舊的 livepatch 可能不提供必要的回撥。

    這在某些情況下可能被視為限制。但在許多其他情況下,它使生活更輕鬆。只有新的累積 livepatch 知道新增/刪除了哪些修復/功能,以及平穩過渡需要哪些特殊操作。

    無論如何,如果呼叫來自所有已啟用補丁的回撥,那麼考慮各種回撥的順序及其相互作用將是一場噩夢。

  • 沒有對影子變數進行特殊處理。Livepatch 作者必須建立自己的規則,說明如何將它們從一個累積補丁傳遞到另一個累積補丁。特別是他們不應該在 module_exit() 函式中盲目地刪除它們。

    一個好的做法可能是在 post-unpatch 回撥中刪除影子變數。只有在 livepatch 正確停用時才會呼叫它。