指標驗證

簡介

指標驗證是一種對特定指標進行簽章的機制。當指標被簽署時,其值和其他值(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 沒有 keydiscriminator 的正確簽章,則內建函式會以目標特定的方式陷入。

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 keyold 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 內建函式 對應到這些指令如下

組譯表示

在組譯層級,已驗證的重定位使用 @AUTH 修飾符表示

    .quad _target@AUTH(<key>,<discriminator>[,addr])

其中

  • key 是 Armv8.3-A 金鑰識別碼(iaibdadb

  • 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 |