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 有兩個階段的放寬,具體取決於核心如何使用使用者地址

  1. 核心未訪問但用於地址空間管理的使用者地址(例如,mprotect(), madvise())。在此上下文中允許使用有效標記指標,但存在以下例外

    • brk(), mmap()mremap()new_address 引數,因為它們有可能與現有使用者地址別名。

      注意:此行為在 v5.6 中發生了更改,因此一些早期的核心可能會錯誤地接受 brk()mmap()mremap() 系統呼叫的有效標記指標。

    • UFFDIO_* ioctl()``s range.startstartdst 引數用於從 userfaultfd() 獲得的檔案描述符,因為隨後透過讀取檔案描述符獲得的故障地址將取消標記,否則可能會使不識別標記的程式感到困惑。

      注意:此行為在 v5.14 中發生了更改,因此一些早期的核心可能會錯誤地接受此係統呼叫的有效標記指標。

  2. 核心訪問的使用者地址(例如 write())。此 ABI 放寬預設情況下處於停用狀態,應用程式執行緒需要透過 prctl() 顯式啟用它,如下所示

    • PR_SET_TAGGED_ADDR_CTRL:為呼叫執行緒啟用或停用 AArch64 標記地址 ABI。

      (unsigned int) arg2 引數是一個位掩碼,描述了使用的控制模式

      • PR_TAGGED_ADDR_ENABLE:啟用 AArch64 標記地址 ABI。預設狀態為停用。

      引數 arg3arg4arg5 必須為 0。

    • PR_GET_TAGGED_ADDR_CTRL:獲取呼叫執行緒的 AArch64 標記地址 ABI 的狀態。

      引數 arg2arg3arg4arg5 必須為 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;
}