虛擬對映核心棧支援¶
- 作者:
Shuah Khan <skhan@linuxfoundation.org>
概述¶
這是來自程式碼和原始補丁系列的資訊彙編,這些補丁系列引入了虛擬對映核心棧功能 <https://lwn.net/Articles/694348/>
簡介¶
核心棧溢位通常難以除錯,並使核心容易受到攻擊。問題可能在稍後出現,從而難以隔離和找到根本原因。
帶有保護頁的虛擬對映核心棧使核心棧溢位能夠立即被捕獲,而不是導致難以診斷的損壞。
HAVE_ARCH_VMAP_STACK和VMAP_STACK配置選項啟用了對帶有保護頁的虛擬對映棧的支援。 此功能在棧溢位時會導致可靠的故障。 溢位後堆疊跟蹤的可用性以及對溢位本身的響應取決於體系結構。
注意
截至本文撰寫之時,arm64、powerpc、riscv、s390、um和x86都支援VMAP_STACK。
HAVE_ARCH_VMAP_STACK¶
可以支援虛擬對映核心棧的體系結構應啟用此bool配置選項。 要求是
vmalloc空間必須足夠大,才能容納許多核心棧。 這可能會排除許多32位體系結構。
vmalloc空間中的棧需要可靠地工作。 例如,如果vmap頁表是按需建立的,則此機制需要在棧指向具有未填充頁表的虛擬地址時工作,或者arch程式碼(最可能是switch_to()和switch_mm())需要確保棧的頁表條目在可能未填充的棧上執行之前已填充。
如果棧溢位到保護頁中,則應發生一些合理的事情。 “合理”的定義是靈活的,但是立即重新啟動而不記錄任何內容將是不友好的。
VMAP_STACK¶
啟用後,VMAP_STACK bool配置選項會分配虛擬對映的任務棧。 此選項取決於HAVE_ARCH_VMAP_STACK。
如果要使用帶有保護頁的虛擬對映核心棧,請啟用此選項。 這會導致核心棧溢位立即被捕獲,而不是導致難以診斷的損壞。
注意
將此功能與KASAN一起使用需要體系結構支援使用真實的影子記憶體來支援虛擬對映,並且必須啟用KASAN_VMALLOC。
注意
VMAP_STACK啟用後,無法在棧分配的資料上執行DMA。
核心配置選項和依賴項不斷變化。 請參考最新的程式碼庫
Kconfig <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/Kconfig>
分配¶
建立新核心執行緒時,執行緒棧是從頁面級別分配器中的虛擬連續記憶體頁分配的。 這些頁面以PAGE_KERNEL保護對映到連續的核心虛擬空間中。
alloc_thread_stack_node()呼叫__vmalloc_node_range()以使用PAGE_KERNEL保護來分配棧。
分配的棧被快取並在以後被新執行緒重用,因此在將棧分配/釋放給任務時,會手動執行memcg記帳。 因此,在沒有__GFP_ACCOUNT的情況下呼叫__vmalloc_node_range。
快取vm_struct,以便能夠在中斷上下文中啟動執行緒釋放時找到。 可以在中斷上下文中呼叫free_thread_stack()。
在arm64上,所有VMAP的棧都需要具有相同的對齊方式,以確保VMAP的棧溢位檢測能夠正常工作。 體系結構特定的vmap棧分配器會處理此細節。
這不解決中斷棧 - 根據原始補丁
執行緒棧分配是從clone()、fork()、vfork()、kernel_thread()經由kernel_clone()啟動的。 這些是一些用於搜尋程式碼庫以瞭解何時以及如何分配執行緒棧的提示。
大部分程式碼位於:kernel/fork.c <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/fork.c>。
task_struct中的stack_vm_area指標跟蹤虛擬分配的棧,並且非空stack_vm_area指標指示已啟用虛擬對映核心棧。
struct vm_struct *stack_vm_area;
棧溢位處理¶
前導和尾隨保護頁有助於檢測棧溢位。 當棧溢位到保護頁中時,處理程式必須小心不要再次溢位棧。 當呼叫處理程式時,可能只剩下很少的棧空間。
在x86上,這是透過在雙重故障棧上處理指示核心棧溢位的頁面錯誤來完成的。
使用保護頁測試VMAP分配¶
我們如何確保VMAP_STACK實際上是使用前導和尾隨保護頁進行分配的? 以下lkdtm測試可以幫助檢測任何迴歸。
void lkdtm_STACK_GUARD_PAGE_LEADING()
void lkdtm_STACK_GUARD_PAGE_TRAILING()
結論¶
至少在快取命中的情況下,vmalloc棧的percpu快取似乎比高階棧分配快一些。
THREAD_INFO_IN_TASK完全擺脫了體系結構特定的thread_info,並將thread_info(僅包含標誌)和“int cpu”嵌入到task_struct中。
執行緒棧可以在任務死亡後立即釋放(無需等待RCU),然後,如果正在使用vmapped棧,則快取整個棧以在同一cpu上重複使用。