AArch64 標記地址 ABI¶
- 作者:Vincenzo Frascino <vincenzo.frascino@arm.com>
Catalin Marinas <catalin.marinas@arm.com>
日期:2019 年 8 月 21 日
本文件介紹了 AArch64 Linux 上標記地址 ABI 的用法和語義。
1. 簡介¶
在 AArch64 上,預設情況下設定 TCR_EL1.TBI0 位,允許使用者空間 (EL0) 透過具有非零頂位元組的 64 位指標執行記憶體訪問。本文件介紹了 syscall ABI 的放寬,允許使用者空間將某些標記指標傳遞給核心 syscall。
2. AArch64 標記地址 ABI¶
從核心 syscall 介面的角度以及本文件的目的來看,“有效標記指標”是指具有潛在非零頂位元組的指標,該指標引用以以下方式之一獲得的使用者程序地址空間中的地址
mmap()syscall,其中標誌具有
MAP_ANONYMOUS位設定或檔案描述符引用常規檔案(包括
memfd_create()返回的檔案)或/dev/zero
brk()syscall(即程式中斷初始位置(在程序建立時)和其當前位置之間的堆區域)。核心在程序建立期間在程序地址空間中對映的任何記憶體,並具有與上述
mmap()相同的限制(例如,資料、bss、堆疊)。
AArch64 標記地址 ABI 有兩個階段的放寬,具體取決於核心如何使用使用者地址
核心未訪問但用於地址空間管理的使用者地址(例如,
mprotect(),madvise())。在此上下文中允許使用有效標記指標,但存在以下例外brk(),mmap()和mremap()的new_address引數,因為它們有可能與現有使用者地址別名。注意:此行為在 v5.6 中發生了更改,因此一些早期的核心可能會錯誤地接受
brk()、mmap()和mremap()系統呼叫的有效標記指標。UFFDIO_*ioctl()``s 的 range.start、start和dst引數用於從userfaultfd()獲得的檔案描述符,因為隨後透過讀取檔案描述符獲得的故障地址將取消標記,否則可能會使不識別標記的程式感到困惑。注意:此行為在 v5.14 中發生了更改,因此一些早期的核心可能會錯誤地接受此係統呼叫的有效標記指標。
核心訪問的使用者地址(例如
write())。此 ABI 放寬預設情況下處於停用狀態,應用程式執行緒需要透過prctl()顯式啟用它,如下所示PR_SET_TAGGED_ADDR_CTRL:為呼叫執行緒啟用或停用 AArch64 標記地址 ABI。(unsigned int) arg2引數是一個位掩碼,描述了使用的控制模式PR_TAGGED_ADDR_ENABLE:啟用 AArch64 標記地址 ABI。預設狀態為停用。
引數
arg3、arg4和arg5必須為 0。PR_GET_TAGGED_ADDR_CTRL:獲取呼叫執行緒的 AArch64 標記地址 ABI 的狀態。引數
arg2、arg3、arg4和arg5必須為 0。
上面描述的 ABI 屬性是執行緒範圍的,在 clone() 和 fork() 上繼承,並在 exec() 上清除。
如果透過
sysctl abi.tagged_addr_disabled=1全域性停用 AArch64 標記地址 ABI,則呼叫prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0)返回-EINVAL。預設的sysctl abi.tagged_addr_disabled配置為 0。
當為執行緒啟用 AArch64 標記地址 ABI 時,保證以下行為
除第 3 節中提到的情況外,所有 syscall 都可以接受任何有效標記指標。
無效標記指標的 syscall 行為未定義:它可能會導致返回錯誤程式碼,引發(致命)訊號或其他故障模式。
有效標記指標的 syscall 行為與相應的未標記指標相同。
有關 AArch64 上標記指標含義的定義,請參見AArch64 Linux 中的標記虛擬地址。
3. AArch64 標記地址 ABI 異常¶
無論 ABI 是否放寬,以下系統呼叫引數都必須未標記
prctl()除了直接或間接作為引數傳遞給核心訪問的使用者資料指標。ioctl()除了直接或間接作為引數傳遞給核心訪問的使用者資料指標。shmat()和shmdt()。brk()(自核心 v5.6 起)。mmap()(自核心 v5.6 起)。mremap(),new_address引數(自核心 v5.6 起)。
任何使用非零標記指標的嘗試都可能導致返回錯誤程式碼,引發(致命)訊號或其他故障模式。
4. 正確用法示例¶
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#define PR_SET_TAGGED_ADDR_CTRL 55
#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
#define TAG_SHIFT 56
int main(void)
{
int tbi_enabled = 0;
unsigned long tag = 0;
char *ptr;
/* check/enable the tagged address ABI */
if (!prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0))
tbi_enabled = 1;
/* memory allocation */
ptr = mmap(NULL, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED)
return 1;
/* set a non-zero tag if the ABI is available */
if (tbi_enabled)
tag = rand() & 0xff;
ptr = (char *)((unsigned long)ptr | (tag << TAG_SHIFT));
/* memory access to a tagged address */
strcpy(ptr, "tagged pointer\n");
/* syscall with a tagged pointer */
write(1, ptr, strlen(ptr));
return 0;
}