避免 SYSRET 和 IRETQ 指示發生問題

在 x86-64 上,核心會使用 SYSRETIRETQ 指令分別從系統呼叫和中斷中傳回。請務必小心,不要在這些操作說明中使用非標準回傳位址 (至少在 Intel CPU 上),因為這會導致指令在核心模式下出錯,並不安全。相較之下,在 AMD CPU 中使用非標準傳回地址時,SYSRET 會錯誤讓使用者模式運作。

這些操作說明會在核心模式下發生錯誤,其中一個問題是,在中斷或系統呼叫處理機制結束時,操作說明會在 gs 註冊從核心 x86_percpu 變為由使用者空間控管的值後出現。當核心模式發生例外狀況時,由於目前的 gs 註冊屬於核心,因此不會變更 gs 登錄。這會讓核心透過使用者控制的 x86_percpu 結構處理錯誤,並輕鬆導致核心程式碼執行。

通常的非負數非標準位址最低為 0x0000800000000000 (== 1 << 47)。如果使用者程序可能會使系統呼叫傳回地址不是標準網址,其中一種方法是將 4K 執行檔對應至該位址 (位於 0x00007ffffffff000) 下方、在該頁面結尾加入 SYSCALL 指令,然後執行 SYSCALL 指令。

如何避免發生這個問題:

  • 如果下列網頁的虛擬位址不是標準網址,我們不允許對應網頁。

  • 我們不允許使用 zx_thread_write_state()RIP 註冊設為非標準位址。

  • 不允許將執行緒進入點設為 ThreadDispatcher::MakeRunnable() 中的非標準位址。

  • 我們不允許在 zx_thread_start()zx_process_start() 中設定非使用者空間的地址。