요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
=======================================
Pointer authentication in AArch64 Linux
=======================================
Author: Mark Rutland <[email protected]>
Date: 2017-07-19
This document briefly describes the provision of pointer authentication
functionality in AArch64 Linux.
Architecture overview
---------------------
The ARMv8.3 Pointer Authentication extension adds primitives that can be
used to mitigate certain classes of attack where an attacker can corrupt
the contents of some memory (e.g. the stack).
The extension uses a Pointer Authentication Code (PAC) to determine
whether pointers have been modified unexpectedly. A PAC is derived from
a pointer, another value (such as the stack pointer), and a secret key
held in system registers.
The extension adds instructions to insert a valid PAC into a pointer,
and to verify/remove the PAC from a pointer. The PAC occupies a number
of high-order bits of the pointer, which varies dependent on the
configured virtual address size and whether pointer tagging is in use.
A subset of these instructions have been allocated from the HINT
encoding space. In the absence of the extension (or when disabled),
these instructions behave as NOPs. Applications and libraries using
these instructions operate correctly regardless of the presence of the
extension.
The extension provides five separate keys to generate PACs - two for
instruction addresses (APIAKey, APIBKey), two for data addresses
(APDAKey, APDBKey), and one for generic authentication (APGAKey).
Basic support
-------------
When CONFIG_ARM64_PTR_AUTH is selected, and relevant HW support is
present, the kernel will assign random key values to each process at
exec*() time. The keys are shared by all threads within the process, and
are preserved across fork().
Presence of address authentication functionality is advertised via
HWCAP_PACA, and generic authentication functionality via HWCAP_PACG.
The number of bits that the PAC occupies in a pointer is 55 minus the
virtual address size configured by the kernel. For example, with a
virtual address size of 48, the PAC is 7 bits wide.
When ARM64_PTR_AUTH_KERNEL is selected, the kernel will be compiled
with HINT space pointer authentication instructions protecting
function returns. Kernels built with this option will work on hardware
with or without pointer authentication support.
In addition to exec(), keys can also be reinitialized to random values
using the PR_PAC_RESET_KEYS prctl. A bitmask of PR_PAC_APIAKEY,
PR_PAC_APIBKEY, PR_PAC_APDAKEY, PR_PAC_APDBKEY and PR_PAC_APGAKEY
specifies which keys are to be reinitialized; specifying 0 means "all
keys".
Debugging
---------
When CONFIG_ARM64_PTR_AUTH is selected, and HW support for address
authentication is present, the kernel will expose the position of TTBR0
PAC bits in the NT_ARM_PAC_MASK regset (struct user_pac_mask), which
userspace can acquire via PTRACE_GETREGSET.
The regset is exposed only when HWCAP_PACA is set. Separate masks are
exposed for data pointers and instruction pointers, as the set of PAC
bits can vary between the two. Note that the masks apply to TTBR0
addresses, and are not valid to apply to TTBR1 addresses (e.g. kernel
pointers).
Additionally, when CONFIG_CHECKPOINT_RESTORE is also set, the kernel
will expose the NT_ARM_PACA_KEYS and NT_ARM_PACG_KEYS regsets (struct
user_pac_address_keys and struct user_pac_generic_keys). These can be
used to get and set the keys for a thread.
Virtualization
--------------
Pointer authentication is enabled in KVM guest when each virtual cpu is
initialised by passing flags KVM_ARM_VCPU_PTRAUTH_[ADDRESS/GENERIC] and
requesting these two separate cpu features to be enabled. The current KVM
guest implementation works by enabling both features together, so both
these userspace flags are checked before enabling pointer authentication.
The separate userspace flag will allow to have no userspace ABI changes
if support is added in the future to allow these two features to be
enabled independently of one another.
As Arm Architecture specifies that Pointer Authentication feature is
implemented along with the VHE feature so KVM arm64 ptrauth code relies
on VHE mode to be present.
Additionally, when these vcpu feature flags are not set then KVM will
filter out the Pointer Authentication system key registers from
KVM_GET/SET_REG_* ioctls and mask those features from cpufeature ID
register. Any attempt to use the Pointer Authentication instructions will
result in an UNDEFINED exception being injected into the guest.
Enabling and disabling keys
---------------------------
The prctl PR_PAC_SET_ENABLED_KEYS allows the user program to control which
PAC keys are enabled in a particular task. It takes two arguments, the
first being a bitmask of PR_PAC_APIAKEY, PR_PAC_APIBKEY, PR_PAC_APDAKEY
and PR_PAC_APDBKEY specifying which keys shall be affected by this prctl,
and the second being a bitmask of the same bits specifying whether the key
should be enabled or disabled. For example::
prctl(PR_PAC_SET_ENABLED_KEYS,
PR_PAC_APIAKEY | PR_PAC_APIBKEY | PR_PAC_APDAKEY | PR_PAC_APDBKEY,
PR_PAC_APIBKEY, 0, 0);
disables all keys except the IB key.
The main reason why this is useful is to enable a userspace ABI that uses PAC
instructions to sign and authenticate function pointers and other pointers
exposed outside of the function, while still allowing binaries conforming to
the ABI to interoperate with legacy binaries that do not sign or authenticate
pointers.
The idea is that a dynamic loader or early startup code would issue this
prctl very early after establishing that a process may load legacy binaries,
but before executing any PAC instructions.
For compatibility with previous kernel versions, processes start up with IA,
IB, DA and DB enabled, and are reset to this state on exec(). Processes created
via fork() and clone() inherit the key enabled state from the calling process.
It is recommended to avoid disabling the IA key, as this has higher performance
overhead than disabling any of the other keys.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
아키텍처 개요
1-40저자: Mark Rutland, 2017-07-19. 이 문서는 AArch64 Linux의 pointer authentication 제공 방식을 간략히 설명합니다. ARMv8.3 Pointer Authentication extension은 공격자가 stack 같은 memory 내용을 변조할 수 있을 때 발생하는 일부 공격을 완화하는 primitive를 추가합니다.
Pointer Authentication Code(PAC)는 pointer가 예기치 않게 바뀌었는지 판정합니다. PAC는 pointer, stack pointer 같은 modifier 값, system register에 보관한 secret key로부터 계산됩니다. Extension은 올바른 PAC를 pointer에 넣고, PAC를 검증해 제거하는 명령을 추가합니다.
PAC는 pointer의 상위 bit 일부를 사용하며, 폭은 설정된 virtual address size와 pointer tagging 사용 여부에 따라 달라집니다. 일부 명령은 `HINT` encoding 공간에 배치되어 기능이 없거나 꺼져 있으면 `NOP`처럼 동작합니다. 따라서 이 명령을 쓰는 application과 library는 extension 존재 여부와 무관하게 실행할 수 있습니다.
| Key | 대상 |
|---|---|
| `APIAKey` | Instruction address A |
| `APIBKey` | Instruction address B |
| `APDAKey` | Data address A |
| `APDBKey` | Data address B |
| `APGAKey` | Generic authentication |
기본 지원
41-67`CONFIG_ARM64_PTR_AUTH`가 선택되고 관련 hardware가 있으면 커널은 `exec*()` 때 process마다 무작위 key를 할당합니다. Key는 process의 모든 thread가 공유하고 `fork()` 뒤에도 유지됩니다.
Address authentication은 `HWCAP_PACA`, generic authentication은 `HWCAP_PACG`로 광고합니다. Pointer에서 PAC가 차지하는 bit 수는 `55 - kernel VA size`입니다. 예를 들어 VA size가 48이면 PAC 폭은 7비트입니다.
`ARM64_PTR_AUTH_KERNEL`을 선택하면 function return을 보호하는 HINT-space pointer authentication 명령으로 커널을 compile합니다. 이 kernel은 pointer authentication 지원 여부와 무관하게 동작합니다.
`exec()` 외에도 `PR_PAC_RESET_KEYS` prctl로 key를 새 무작위 값으로 초기화할 수 있습니다.
| Reset flag | 대상 |
|---|---|
| `PR_PAC_APIAKEY` | `APIAKey` |
| `PR_PAC_APIBKEY` | `APIBKey` |
| `PR_PAC_APDAKEY` | `APDAKey` |
| `PR_PAC_APDBKEY` | `APDBKey` |
| `PR_PAC_APGAKEY` | `APGAKey`; bitmask가 0이면 모든 key |
Debugging interface
68-87`CONFIG_ARM64_PTR_AUTH`가 선택되고 address authentication hardware가 있으면 커널은 `NT_ARM_PAC_MASK` regset(`struct user_pac_mask`)에 TTBR0 PAC bit 위치를 공개합니다. 사용자 공간은 `PTRACE_GETREGSET`으로 읽을 수 있습니다.
이 regset은 `HWCAP_PACA`가 설정된 경우에만 나타납니다. Data pointer와 instruction pointer의 PAC bit 집합이 다를 수 있으므로 mask도 따로 제공합니다. Mask는 TTBR0 주소에만 적용되며 kernel pointer 같은 TTBR1 주소에는 유효하지 않습니다.
`CONFIG_CHECKPOINT_RESTORE`도 설정되면 `NT_ARM_PACA_KEYS`와 `NT_ARM_PACG_KEYS` regset(`struct user_pac_address_keys`, `struct user_pac_generic_keys`)을 공개하여 thread key를 읽고 쓸 수 있게 합니다.
KVM virtualization
88-110KVM guest의 각 vCPU를 초기화할 때 `KVM_ARM_VCPU_PTRAUTH_[ADDRESS/GENERIC]` flag로 두 CPU feature를 모두 요청하면 pointer authentication을 켭니다. 현재 구현은 두 기능을 함께 enable하므로 두 사용자 공간 flag를 모두 검사합니다. 별도 flag를 유지하는 이유는 나중에 독립 enable을 지원하더라도 userspace ABI를 바꾸지 않기 위해서입니다.
Arm Architecture는 Pointer Authentication이 VHE와 함께 구현된다고 규정하므로 KVM arm64 ptrauth code는 VHE mode에 의존합니다.
vCPU feature flag가 없으면 KVM은 `KVM_GET/SET_REG_*` ioctl에서 Pointer Authentication system key register를 제거하고 cpufeature ID register에서도 기능을 mask합니다. Guest가 Pointer Authentication 명령을 실행하면 `UNDEFINED` exception을 주입합니다.
Key 활성화와 비활성화
111-126`PR_PAC_SET_ENABLED_KEYS` prctl은 task에서 어떤 PAC key를 enable할지 제어합니다. 첫 argument는 `PR_PAC_APIAKEY`, `PR_PAC_APIBKEY`, `PR_PAC_APDAKEY`, `PR_PAC_APDBKEY` 중 영향을 받을 key의 bitmask이고, 두 번째 argument는 같은 bit 중 실제로 enable할 key의 bitmask입니다.
prctl(PR_PAC_SET_ENABLED_KEYS,
PR_PAC_APIAKEY | PR_PAC_APIBKEY | PR_PAC_APDAKEY | PR_PAC_APDBKEY,
PR_PAC_APIBKEY, 0, 0);
이 예는 IB key만 남기고 나머지 key를 모두 disable합니다.
ABI 호환성과 초기 상태
127-142이 제어는 PAC 명령으로 function pointer와 function 밖에 노출되는 다른 pointer를 sign하고 authenticate하는 userspace ABI를 쓰면서도, pointer를 sign하지 않는 legacy binary와 상호 운용할 때 유용합니다.
Dynamic loader 또는 early startup code는 process가 legacy binary를 load할 수 있음을 확인한 직후, 어떤 PAC 명령도 실행하기 전에 이 prctl을 호출해야 합니다.
이전 kernel과의 호환성을 위해 process는 IA, IB, DA, DB가 enable된 상태로 시작하며 `exec()` 때 이 상태로 reset됩니다. `fork()`와 `clone()`으로 만든 process는 caller의 key-enabled state를 상속합니다.
IA key를 disable하면 다른 key를 disable할 때보다 성능 overhead가 크므로 피하는 것이 권장됩니다.
요약과 해설
pointer-authentication.rst:1-142Pointer Authentication은 pointer와 modifier, secret key에서 PAC를 계산해 pointer 변조를 탐지합니다. Linux는 process별 무작위 key, hwcap, ptrace regset, KVM feature flag, prctl을 하나의 ABI로 묶어 보호 기능과 legacy binary 상호 운용을 함께 제공합니다.
Pointer에 code를 넣고 사용할 때 검증한 뒤 원래 pointer를 복원합니다.
기능 탐지, debugging, virtualization, policy 제어 경로입니다.