指標認證

簡介

指標認證是一種機制,透過此機制,某些指標會被簽署。當指標被簽署時,其值與其他值(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 沒有針對 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 金鑰簽署 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 內建函數對應到這些指令,如下所示

組譯表示法

在組譯層級,已驗證的重定位使用 @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 |