指標認證¶
簡介¶
指標認證是一種機制,透過此機制,某些指標會被簽署。當指標被簽署時,其值與其他值(pepper 和 salt)的密碼雜湊會儲存在該指標的未使用位元中。
在使用指標之前,需要對其進行驗證,即檢查其簽章。這可以防止來源不明的指標值被用來替換已簽署的指標值。
如需更多詳細資訊,請參閱 clang 文件頁面關於指標認證的說明。
在 IR 層級,它使用以下方式表示:
一組內建函數(用於簽署/驗證指標)
已簽署的指標常數(用於簽署全域變數)
呼叫運算元組(用於驗證被呼叫的指標)
一組函數屬性(用於描述哪些指標已簽署以及如何簽署,以控制後端的隱式程式碼產生,以及在中階最佳化器中保留不變性)
目前的實作利用了 Armv8.3-A PAuth/指標認證碼 指令,位於 AArch64 後端中。此支援用於實作 Darwin arm64e ABI,以及 PAuth ABI ELF 擴充。
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
金鑰簽署 blockaddress,並使用從父函數名稱衍生的整數鑑別器,使用 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 中,它是將MOVK
放入高 16 位元。因此,這將混合中使用的整數鑑別器的寬度限制為 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 |