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 位指针执行内存访问。本文档描述了系统调用 ABI 的放宽,允许用户空间将某些标记指针传递给内核系统调用。
2. AArch64 标记地址 ABI¶
从内核系统调用接口的角度来看,并且为了本文档的目的,“有效的标记指针”是指具有潜在非零高字节的指针,该指针引用用户进程地址空间中的地址,该地址以下列方式之一获得
mmap()
系统调用,其中标志设置了
MAP_ANONYMOUS
位或文件描述符引用常规文件(包括由
memfd_create()
返回的那些文件)或/dev/zero
brk()
系统调用(即在进程创建时程序中断的初始位置与其当前位置之间的堆区域)。在进程创建期间内核在进程地址空间中映射的任何内存,并且具有与上述
mmap()
相同的限制(例如,数据、bss、堆栈)。
AArch64 标记地址 ABI 有两个阶段的放宽,具体取决于内核如何使用用户地址
内核未访问但用于地址空间管理的用户地址(例如,
mprotect()
、madvise()
)。在这种情况下,允许使用有效的标记指针,但以下情况除外brk()
、mmap()
和mremap()
的new_address
参数,因为这些可能会与现有用户地址别名。注意:此行为在 v5.6 中已更改,因此一些较早的内核可能不正确地接受
brk()
、mmap()
和mremap()
系统调用的有效标记指针。UFFDIO_*
ioctl()``s 用于 从 ``userfaultfd()
获取的文件描述符的range.start
、start
和dst
参数,因为随后通过读取文件描述符获得的错误地址将是未标记的,这可能会混淆未感知标记的程序。注意:此行为在 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() 上清除。
如果 AArch64 标记地址 ABI 通过
sysctl abi.tagged_addr_disabled=1
全局禁用,则调用prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0)
返回-EINVAL
。默认的sysctl abi.tagged_addr_disabled
配置为 0。
当为线程启用 AArch64 标记地址 ABI 时,保证以下行为
除了第 3 节中提到的情况外,所有系统调用都可以接受任何有效的标记指针。
对于无效的标记指针,系统调用行为是未定义的:它可能会导致返回错误代码、引发(致命)信号或其他故障模式。
对于有效的标记指针,系统调用行为与相应的未标记指针相同。
有关 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;
}