指標驗證¶
簡介¶
指標驗證是一種對特定指標進行簽章的機制。當指標被簽署時,其值和其他值(pepper 和 salt)的加密雜湊值會儲存在該指標未使用的位元中。
在使用指標之前,需要先對其進行驗證,即檢查其簽章。這可以防止來源不明的指標值被用來替換已簽署的指標值。
如需詳細資訊,請參閱 clang 文件頁面中的 指標驗證。
在 IR 層級,它使用以下方式表示
一組 內建函式(用於簽署/驗證指標)
一個 已簽署的指標常數(用於簽署全域變數)
一個 呼叫運算元套件(用於驗證被呼叫的指標)
一組 函式屬性(用於描述哪些指標已簽署以及如何簽署,以控制後端中的隱式程式碼產生,以及在中介最佳化器中保留不變量)
目前的實作利用 Armv8.3-A PAuth/指標驗證程式碼 指令在 AArch64 後端 中的實作。此支援用於實作 Darwin arm64e ABI,以及 ELF 的 PAuth ABI 擴充功能。
LLVM IR 表示法¶
內建函式¶
LLVM 提供這些內建函式來公開指標驗證操作。
‘llvm.ptrauth.sign
’¶
語法:¶
declare i64 @llvm.ptrauth.sign(i64 <value>, i32 <key>, i64 <discriminator>)
概觀:¶
‘llvm.ptrauth.sign
’ 內建函式會簽署原始指標。
參數:¶
value
參數是要簽署的原始指標值。key
參數是用於產生已簽署值的索引鍵識別碼。discriminator
參數是要用作區分器的額外多樣性資料(整數、位址或兩者的混合)。
語義:¶
‘llvm.ptrauth.sign
’ 內建函式會實作 sign
_ 作業。它會傳回已簽署的值。
如果 value
已經是已簽署的值,則行為未定義。
如果 value
不是適合 key
的指標值,則行為未定義。
‘llvm.ptrauth.auth
’¶
語法:¶
declare i64 @llvm.ptrauth.auth(i64 <value>, i32 <key>, i64 <discriminator>)
概述:¶
‘llvm.ptrauth.auth
’ 內建函式會驗證已簽署的指標。
引數:¶
value
引數是要驗證的已簽署指標值。 key
引數是用於產生已簽署值的索引鍵識別碼。 discriminator
引數是要用作鑑別器的額外多樣性資料。
語意:¶
‘llvm.ptrauth.auth
’ 內建函式會實作 auth
_ 作業。它會傳回原始指標值。如果 value
沒有 key
和 discriminator
的正確簽章,則內建函式會以目標特定的方式陷入。
‘llvm.ptrauth.strip
’¶
語法:¶
declare i64 @llvm.ptrauth.strip(i64 <value>, i32 <key>)
概述:¶
‘llvm.ptrauth.strip
’ 內建函式會從可能已簽署的指標中去除嵌入式簽章。
引數:¶
value
引數是要去除的已簽署指標值。 key
引數是用於產生已簽署值的索引鍵識別碼。
語意:¶
‘llvm.ptrauth.strip
’ 內建函式會實作 strip
_ 作業。它會傳回原始指標值。它**不會**檢查簽章是否有效。
key
應識別適用於 value
的索引鍵,如目標特定的 索引鍵 中所定義)。
如果 value
是原始指標值,則會按原樣傳回(前提是 key
適用於指標)。
如果 value
不是適合 key
的指標值,則行為是目標特定的。
如果 value
是已簽署的指標值,但 key
未識別用於產生 value
的相同索引鍵,則行為是目標特定的。
‘llvm.ptrauth.resign
’¶
語法:¶
declare i64 @llvm.ptrauth.resign(i64 <value>,
i32 <old key>, i64 <old discriminator>,
i32 <new key>, i64 <new discriminator>)
概觀:¶
‘llvm.ptrauth.resign
’ 內建函式使用不同的金鑰和多樣性數據重新簽署已簽署的指標。
參數:¶
value
參數是要驗證的已簽署指標值。old key
參數是用於產生已簽署值的密鑰識別碼。old discriminator
參數是要用作驗證操作中鑑別器的額外多樣性數據。new key
參數是用於產生重新簽署值的密鑰識別碼。new discriminator
參數是要用作簽署操作中鑑別器的額外多樣性數據。
語義:¶
‘llvm.ptrauth.resign
’ 內建函式執行組合的 auth
_ 和 sign
_ 操作,而不公開中間原始指標。它會傳回已簽署的指標值。如果 value
沒有針對 old key
和 old discriminator
的正確簽章,則內建函式會以特定於目標的方式陷入困境。
‘llvm.ptrauth.sign_generic
’¶
語法:¶
declare i64 @llvm.ptrauth.sign_generic(i64 <value>, i64 <discriminator>)
概觀:¶
‘llvm.ptrauth.sign_generic
’ 內建函式計算任意數據的通用簽章。
參數:¶
value
參數是要簽署的任意數據值。discriminator
參數是要用作鑑別器的額外多樣性數據。
語義:¶
‘llvm.ptrauth.sign_generic
’ 內建函式計算給定值和額外多樣性數據組合的簽章。
它會傳回完整的簽章值(與具有內嵌部分簽章的已簽署指標值相反)。
與 llvm.ptrauth.sign
相反,它不會將 value
解釋為指標值。相反,它是一個任意的數據值。
‘llvm.ptrauth.blend
’¶
語法:¶
declare i64 @llvm.ptrauth.blend(i64 <address discriminator>, i64 <integer discriminator>)
概觀:¶
‘llvm.ptrauth.blend
’ 內建函式將指標地址鑑別器與一個小的整數鑑別器混合,以產生一個新的「混合」鑑別器。
參數:¶
address discriminator
參數是一個指標值。integer discriminator
參數是一個小的整數,由目標指定。
語義:¶
內建函式 ‘llvm.ptrauth.blend
’ 會將一個小的整數鑑別值與一個指標地址鑑別值組合起來,組合方式由目標實作指定。
常數¶
內建函式 可用於在程式碼中動態產生已簽署的指標,但不能用於由常數引用的已簽署指標,例如,全域初始設定式。
後者使用 ptrauth
常數 表示,該常數描述產生已簽署指標的已驗證重新定位。
ptrauth (ptr CST, i32 KEY, i64 DISC, ptr ADDRDISC)
等同於
%disc = call i64 @llvm.ptrauth.blend(i64 ptrtoint(ptr ADDRDISC to i64), i64 DISC)
%signedval = call i64 @llvm.ptrauth.sign(ptr CST, i32 KEY, i64 %disc)
運算元組合¶
用作間接呼叫目標的函式指標可以在具體化時簽署,並在呼叫之前進行驗證。這可以使用 llvm.ptrauth.auth
內建函式來完成,將其結果饋送到間接呼叫。
但是,這會暴露中間的、未經身份驗證的指標,例如,如果它被溢出到堆疊中。然後攻擊者可以在記憶體中覆蓋指標,從而否定指標身份驗證提供的安全益處。為了防止這種情況,可以使用 ptrauth
運算元組合:它保證中間呼叫目標保留在暫存器中,並且永遠不會儲存到記憶體中。這種強化的好處類似於 llvm.ptrauth.resign
所提供的好處)。
具體來說
define void @f(void ()* %fp) {
call void %fp() [ "ptrauth"(i32 <key>, i64 <data>) ]
ret void
}
在功能上等同於
define void @f(void ()* %fp) {
%fp_i = ptrtoint void ()* %fp to i64
%fp_auth = call i64 @llvm.ptrauth.auth(i64 %fp_i, i32 <key>, i64 <data>)
%fp_auth_p = inttoptr i64 %fp_auth to void ()*
call void %fp_auth_p()
ret void
}
但增加了 %fp_i
、%fp_auth
和 %fp_auth_p
不會儲存到(並從)記憶體載入的保證。
函式屬性¶
某些函式屬性用於描述 IR 中未明確表示的其他指標身份驗證操作。
ptrauth-indirect-gotos
¶
ptrauth-indirect-gotos
指定此函式中的間接 goto 應驗證其目標。在 IR 級別,不需要其他更改。降低 blockaddress
常數 和 indirectbr
指令 時,這會告訴後端分別對指標進行簽署和驗證。
具體方案在 ABI 中不可見。目前,AArch64 後端使用 ASIA
金鑰對區塊地址進行簽署,並使用 SipHash 穩定鑑別值從父函式的名稱派生出一個整數鑑別值
ptrauth_string_discriminator("<function_name> blockaddress")
AArch64 支援¶
AArch64 是目前唯一完全支援指標身份驗證原語的架構,它基於 Armv8.3-A 指令。
Armv8.3-A PAuth 指標身份驗證代碼¶
Armv8.3-A 架構擴展定義了 PAuth 功能,該功能提供對操作指標身份驗證代碼 (PAC) 的指令的支援。
金鑰¶
PAuth 功能支援 5 個金鑰。
其中,4 個金鑰可以互換使用,以指定 IR 構造中使用的金鑰
ASIA
/ASIB
是指令金鑰(分別編碼為 0 和 1)。ASDA
/ASDB
是資料金鑰(分別編碼為 2 和 3)。
ASGA
是一個特殊金鑰,不能明確指定,並且僅在隱式使用時用於實作 llvm.ptrauth.sign_generic
內建函式。
指令¶
上述 IR 內建函式 對應到這些指令如下
llvm.ptrauth.sign
:PAC{I,D}{A,B}{Z,SP,}
llvm.ptrauth.auth
:AUT{I,D}{A,B}{Z,SP,}
llvm.ptrauth.strip
:XPAC{I,D}
llvm.ptrauth.blend
:混合操作的語義由 ABI 指定。在 ELF PAuth ABI 擴展和 arm64e 中,它是一個到高 16 位的MOVK
。因此,這會將混合中使用的整數鑑別器的寬度限制為 16 位。llvm.ptrauth.resign
:AUT*+PAC*
。這些在後端表示為單個偽指令,以確保中間原始指標值不會被洩漏和攻擊。
組譯表示¶
在組譯層級,已驗證的重定位使用 @AUTH
修飾符表示
.quad _target@AUTH(<key>,<discriminator>[,addr])
其中
key
是 Armv8.3-A 金鑰識別碼(ia
、ib
、da
、db
)discriminator
是 16 位無符號鑑別器值addr
表示已驗證的指標是地址鑑別的(也就是說,重定位的目標地址將在用於簽署操作之前混合到discriminator
中)。
舉例來說
_authenticated_reference_to_sym:
.quad _sym@AUTH(db,0)
_authenticated_reference_to_sym_addr_disc:
.quad _sym@AUTH(ia,12,addr)
MachO 物件檔案表示¶
在物件檔案層級,已驗證的重定位使用 ARM64_RELOC_AUTHENTICATED_POINTER
重定位類型(值為 11
)表示。
指標驗證資訊按如下方式編碼到加數中
| 63 | 62 | 61-51 | 50-49 | 48 | 47 - 32 | 31 - 0 |
| -- | -- | ----- | ----- | ------ | --------------- | -------- |
| 1 | 0 | 0 | key | addr | discriminator | addend |
ELF 物件檔案表示¶
在目標檔案層級,已驗證的重定位使用 R_AARCH64_AUTH_ABS64
重定位類型來表示(值為 0xE100
)。
簽署架構編碼在要應用的重定位位置,如下所示
| 63 | 62 | 61:60 | 59:48 | 47:32 | 31:0 |
| ----------------- | -------- | -------- | -------- | ------------- | ------------------- |
| address diversity | reserved | key | reserved | discriminator | reserved for addend |