ToolChain/PACBTI - Debian Wiki
Wiki
FrontPage
RecentChanges
FindPage
HelpContents
ToolChain/PACBTI
Wiki
Comments
Info
Attachments
ToolChain
PACBTI
Contents
Summary
Software support
PAC
BTI
Packages overriding the default build flags
Hardware support
In practice
PAC
BTI
References
Summary
Pointer Authentication (PAC) and Branch Target Identification (BTI) are two
security features available on arm64. They are designed to mitigate
Return-Oriented Programming (ROP) and Jump-Oriented Programming (JOP) attacks
respectively. See
the references
for all details about the attacks and the protection offered. This document is
about the details of how PAC and BTI are enabled in Debian.
Software support
PAC and BTI can be enabled by compiling a program with the GCC flag
-mbranch-protection=standard
. In Debian, the flag has been enabled by default since August 2023 with the upload of dpkg 1.22.0, which added the
branch
setting to the
hardening
area.
Should the need arise, the feature can be disabled for a specific package with:
export DEB_BUILD_MAINT_OPTIONS=hardening=-branch
Additionally, it is possible to unconditionally disable PAC on a system by adding
arm64.nopauth
to the kernel command line, or
arm64.nobti
to disable BTI.
PAC
When it comes to enabling PAC, there is no additional requirement: as long as
the program is built with -mbranch-protection=standard, the resulting ELF file
will include PAC instructions, and CPUs supporting PAC will sign and
authenticate addresses. Among the assembly instructions introduced to implement
PAC, there are some used to sign an address (eg: PACIASP) and others to verify
- or authenticate - an address (eg: AUTIASP, RETAA). To check if a given ELF file
was built with PAC support, one can thus simply search for those instructions.
For example:
objdump -d /usr/bin/ls | grep -E 'pac|aut'
BTI
BTI is different: it is enabled at runtime by the loader only if all execution
units were built with BTI. This means that all
.o
files combined together by
the linker need to be built with BTI support. There is a specific ELF property
set on ELF files that support BTI, and it can be checked as follows:
readelf -n /usr/bin/ls | grep Properties
Properties: AArch64 feature: BTI, PAC
Pretty much all programs are linked using
crtbeginS.o
and
crtendS.o
from
GCC, as well as
crti.o
Scrt1.o
and
crtn.o
from glibc. For this reason, both GCC and glibc need to be built with BTI support in order for any other program to have it too.
All key packages enabled BTI support in July 2024: GCC 13 starting with version 13.3.0-2, GCC 14 with version 14.1.0-4. When it comes to glibc, the first Debian revision built with BTI support was 2.39-5
Package
Version
Date
gcc-13
13.3.0-2
2024-07-08
gcc-14
14.1.0-4
2024-07-10
glibc
2.39-5
2024-07-22
All bugs related to PAC and BTI in Debian should have the
pac-bti
usertag:
Packages overriding the default build flags
Most upstream projects use build tools (eg: GNU Autotools) that honor environment variables such as CFLAGS and similar. Those get PAC/BTI support without any changes, as well as all other useful flags set by
dpkg-buildflags
. However, certain packages have hand-written Makefiles that unconditionally override the build flags. For example:
CFLAGS = -O3 -Wall -Wstrict-prototypes -g
CC = gcc
In the example above the values chosen by upstream are used, no matter what Debian (or the user) specifies in
$CFLAGS
and
$CC
Instead of doing so, one can use the
Conditional Variable Assignment
operator
?=
to ensure that the CFLAGS and CC environment variables are honored if set:
CFLAGS ?= -O3 -Wall -Wstrict-prototypes -g
CC ?= gcc
If the intent is to
append
certain flags to a variable such as CFLAGS,
+=
should be used instead:
CFLAGS += -Wstrict-prototypes
Hardware support
Not all CPUs support PAC and/or BTI. To check for PAC support one can look for
the flags
paca
and
pacg
in
/proc/cpuinfo
. For BTI support, the flag is
bti
Additionally, the kernel prints information about all the detected CPU features. For PAC, search for a line like the following:
CPU features: detected: Address authentication (architected QARMA5 algorithm)
For BTI, look for:
CPU features: detected: Branch Target Identification
To implement the features, the Arm architecture has been extended with
additional assembly instructions. They are defined in NOP space, which means
that CPUs without PAC or BTI support can still execute code built with the new
instructions - they simply will execute some
nop
instructions instead.
In practice
PAC
The following program simulates a ROP attack corrupting the
LR
(or
x30
) registry, used on arm64 to store the return address of a function. Building the code without PAC results in the string
ROP attack worked
being printed.
#include
#include
#include
void target() {
printf("ROP attack worked\n");
exit(-1);
void hello() {
int xxx;
printf("Trying to overwrite return address of hello()\n");
*((&xxx) - 0x5) = (intptr_t)target;
int main() {
hello();
Building the program with PAC enabled and running it results in a crash. Which crash precisely depends on the specific CPU in use. On systems with
FEAT_FPAC
the
autiasp
instruction fails with a SIGILL when failing to validate the value stored in
LR
, for example in the case of
hello
the program would crash before
ret
is called:
0xaaaaaaaa07ec
0xaaaaaaaa07f0
0xaaaaaaaa07f4
On systems without
FEAT_FPAC
the
autiasp
instruction itself does not cause an exception, but instead it leaves a faulting value in
LR
. The program would therefore segfault at the very beginning of target when executing the
paciasp
instruction. See section
D8.8.4 Faulting on pointer authentication
of the
Arm Architecture Reference Manual
for the details.
BTI
The files under
/proc/$pid/smaps
can be inspected to check which mappings have BTI enabled. BTI support is shown as
bt
under
VmFlags
ffff980e0000-ffff98274000 r-xp 00000000 fe:01 8020 /usr/lib/aarch64-linux-gnu/libc.so.6
Size: 1616 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 1204 kB
Pss: 69 kB
Pss_Dirty: 0 kB
Shared_Clean: 1204 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 1204 kB
Anonymous: 0 kB
KSM: 0 kB
LazyFree: 0 kB
AnonHugePages: 0 kB
ShmemPmdMapped: 0 kB
FilePmdMapped: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
Locked: 0 kB
THPeligible: 0
VmFlags: rd ex mr mw me bt
Additionally, the following
SystemTap
script can be used to monitor the activity of the loader at runtime, see which shared libraries are being loaded and whether the have BTI enabled:
global loaded, enabled;
probe process("/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1").function("_dl_map_object") {
f = user_string($loader->l_name);
if (strlen(f) > 0 && !loaded[f]) {
printf("Loaded %s\n", f);
loaded[f] = 1
probe process("/usr/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1").function("_dl_bti_protect") {
f = user_string($map->l_name);
if (strlen(f) > 0 && !enabled[f]) {
printf("BTI enabled for %s\n", f);
enabled[f] = 1;
In the following example,
libm.so.6
is loaded with BTI support while
libm.so.6
is loaded without it.
BTI enabled for /lib/aarch64-linux-gnu/libm.so.6
Loaded /lib/aarch64-linux-gnu/libpam_misc.so.0
Loaded /lib/aarch64-linux-gnu/libm.so.6
References
Steve Capper's presentation at Cambridge MiniDebConf 2023
Providing protection for complex software
Enabling PAC and BTI on AArch64 for Linux by Bill Roberts
CategoryPermalink
: mentioned in the
Release notes for trixie
ToolChain/PACBTI (
last modified 2026-02-11 20:31:43
Changes made after 24 July 2025 00:00 UTC are available under
Creative Commons Attribution-ShareAlike 4.0 International
unless otherwise noted.
Debian
, Wiki
team
bugs
and
config
MoinMoin
and
Python
, with hosting provided by
Metropolitan Area Network Darmstadt
US