From 53c2af4da7b412915852c8969bd58e2cb6fd0949 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Tue, 12 May 2026 17:17:48 -0700 Subject: [PATCH 1/2] Advertise pointer authentication support to GDB GDB uses the values of the provided pointer authentication mask registers to mask return addresses from the stack trace. Because we weren't providing these masks, the return addresses in the stack trace were incorrect in programs built with -mbranch-protection=pac-ret, such as most of Fedora. Fix it by advertising pointer authentication to GDB, and providing the correct masks. Fixes the following tests on PAC platforms: fork_exec_info_thr fork_exec_info_thr-no-syscallbuf vdso_clock_gettime_stack vdso_time_stack --- src/ExtraRegisters.cc | 15 +++++++++++++++ src/GdbServerConnection.cc | 7 +++++++ src/GdbServerConnection.h | 3 ++- src/GdbServerRegister.h | 6 +++++- src/TargetDescription.cc | 6 ++++++ src/TargetDescription.h | 1 + src/kernel_supplement.h | 4 ++++ 7 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/ExtraRegisters.cc b/src/ExtraRegisters.cc index 1b585cbaa16..5ba479d4a99 100644 --- a/src/ExtraRegisters.cc +++ b/src/ExtraRegisters.cc @@ -275,6 +275,21 @@ size_t ExtraRegisters::read_register(uint8_t* buf, GdbServerRegister regno, } else if (regno == DREG_FPCR) { reg_data = RegData(offsetof(ARM64Arch::user_fpsimd_state, fpcr), sizeof(uint32_t)); +#ifdef __aarch64__ + } else if (regno == DREG_PAUTH_DMASK || regno == DREG_PAUTH_CMASK) { + uint64_t ptr = 1ULL << 55; + // The XPAC instruction will copy bit 55 of the argument into the PAC mask + // bits, so ptr will be set to the mask plus bit 55. + if (regno == DREG_PAUTH_DMASK) { + __asm__ __volatile__("xpacd %0" : "+r"(ptr)); + } else { + __asm__ __volatile__("xpaci %0" : "+r"(ptr)); + } + uint64_t mask = ptr & ~(1ULL << 55); + *defined = true; + memcpy(buf, &mask, sizeof(mask)); + return sizeof(mask); +#endif } else { *defined = false; return 0; diff --git a/src/GdbServerConnection.cc b/src/GdbServerConnection.cc index e67ee06bbfd..dd8cc628a72 100644 --- a/src/GdbServerConnection.cc +++ b/src/GdbServerConnection.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include "ScopedFd.h" #include "TargetDescription.h" #include "core.h" +#include "kernel_supplement.h" #include "log.h" using namespace std; @@ -106,6 +108,11 @@ static uint32_t get_cpu_features(SupportedArch arch) { } case aarch64: cpu_features = GdbServerConnection::CPU_AARCH64; +#ifdef __aarch64__ + if (getauxval(AT_HWCAP) & HWCAP_PACA) { + cpu_features |= GdbServerConnection::CPU_PAUTH; + } +#endif break; default: FATAL() << "Unknown architecture"; diff --git a/src/GdbServerConnection.h b/src/GdbServerConnection.h index e6204d4c09d..08db82e114c 100644 --- a/src/GdbServerConnection.h +++ b/src/GdbServerConnection.h @@ -752,7 +752,8 @@ class GdbServerConnection { CPU_AVX = 1 << 1, CPU_AARCH64 = 1 << 2, CPU_PKU = 1 << 3, - CPU_AVX512 = 1 << 4 + CPU_AVX512 = 1 << 4, + CPU_PAUTH = 1 << 5 }; void set_cpu_features(SupportedArch arch); diff --git a/src/GdbServerRegister.h b/src/GdbServerRegister.h index 0234c39349f..a17dc52ad6b 100644 --- a/src/GdbServerRegister.h +++ b/src/GdbServerRegister.h @@ -320,7 +320,11 @@ enum GdbServerRegister { DREG_FPSR, DREG_FPCR, - DREG_NUM_LINUX_AARCH64 = DREG_FPCR + 1, + // aarch64-pauth.xml + DREG_PAUTH_DMASK, + DREG_PAUTH_CMASK, + + DREG_NUM_LINUX_AARCH64, }; } // namespace rr diff --git a/src/TargetDescription.cc b/src/TargetDescription.cc index 17726c8b1ba..22f066a8a08 100644 --- a/src/TargetDescription.cc +++ b/src/TargetDescription.cc @@ -78,6 +78,9 @@ FeatureStream& operator<<(FeatureStream& stream, TargetFeature feature) { case TargetFeature::FPU: stream << "fpu.xml"; break; + case TargetFeature::PAuth: + stream << "pauth.xml"; + break; } stream << R"("/>)" << '\n'; return stream; @@ -103,6 +106,9 @@ TargetDescription::TargetDescription(rr::SupportedArch arch, case rr::aarch64: target_features.push_back(TargetFeature::Core); target_features.push_back(TargetFeature::FPU); + if (cpu_features & rr::GdbServerConnection::CPU_PAUTH) { + target_features.push_back(TargetFeature::PAuth); + } break; } diff --git a/src/TargetDescription.h b/src/TargetDescription.h index e4dcf556400..245a4ff41ed 100644 --- a/src/TargetDescription.h +++ b/src/TargetDescription.h @@ -20,6 +20,7 @@ enum class TargetFeature : uint32_t { AVX512, PKeys, FPU, + PAuth, }; class TargetDescription { diff --git a/src/kernel_supplement.h b/src/kernel_supplement.h index 0c6f8eaaea8..71bf6930f57 100644 --- a/src/kernel_supplement.h +++ b/src/kernel_supplement.h @@ -148,6 +148,10 @@ enum _ptrace_get_syscall_info_op { #define NT_ARM_PACG_KEYS 0x408 #endif +#ifndef HWCAP_PACA +#define HWCAP_PACA (1 << 30) +#endif + // These are defined by the include/linux/errno.h in the kernel tree. // Since userspace doesn't see these errnos in normal operation, that // header apparently isn't distributed with libc. From 96cefe18f7664fd223c3225313564cb243a54a75 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Wed, 13 May 2026 06:58:54 -0700 Subject: [PATCH 2/2] Only include the pauth registers in target_registers() if they're present. --- src/GdbServer.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/GdbServer.cc b/src/GdbServer.cc index 0d4e65a5256..12a3c943f1c 100644 --- a/src/GdbServer.cc +++ b/src/GdbServer.cc @@ -2332,6 +2332,7 @@ const vector& GdbServer::target_registers( bool have_PKU = dbg->cpu_features() & GdbServerConnection::CPU_PKU; bool have_AVX = dbg->cpu_features() & GdbServerConnection::CPU_AVX; bool have_AVX512 = dbg->cpu_features() & GdbServerConnection::CPU_AVX512; + bool have_PAUTH = dbg->cpu_features() & GdbServerConnection::CPU_PAUTH; switch (arch) { case x86: { add_range(GdbServerRegister(0), GdbServerRegister(DREG_ORIG_EAX)); @@ -2368,7 +2369,11 @@ const vector& GdbServer::target_registers( } case aarch64: add_range(GdbServerRegister::DREG_X0, - GdbServerRegister::DREG_NUM_LINUX_AARCH64); + GdbServerRegister::DREG_FPCR); + if (have_PAUTH) { + register_description.push_back(DREG_PAUTH_DMASK); + register_description.push_back(DREG_PAUTH_CMASK); + } break; default: FATAL() << "Unknown architecture";