From 7dac37e8c8b493bb5a5cb553bd8ccb327ca39c2a Mon Sep 17 00:00:00 2001 From: Alex Forsythe Date: Tue, 30 Jun 2026 13:28:40 -0500 Subject: [PATCH] fix: Strip PAC bits from return addresses in POSIX crash handler ARM64 has a security feature called Pointer Authentication Codes (PAC), where the CPU cryptographically signs address values, encoding the signature in the upper bits of the 64-bit value. It can then verify these addresses before jumping to them, to detect stack manipulation etc. at the hardware level. The SDK's in-process crash handler for POSIX reads return addresses as it walks the stack frame. When running arm64 Linux builds in Docker, I noticed that these addresses weren't being correctly resolved to loaded modules for shared libraries. As it turns out, module resolution was failing because the upper bits were set by PAC, causing the resolved address to land outside the load range of the module. Masking out these upper bits to preserve only the lower 48 bits resolved the issue in my tests. --- .../inprocess/crash_handler_inprocess_posix.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/datadog/impl/crash_reporting/handlers/inprocess/crash_handler_inprocess_posix.cpp b/src/datadog/impl/crash_reporting/handlers/inprocess/crash_handler_inprocess_posix.cpp index fafa5c9d..8dbbbb72 100644 --- a/src/datadog/impl/crash_reporting/handlers/inprocess/crash_handler_inprocess_posix.cpp +++ b/src/datadog/impl/crash_reporting/handlers/inprocess/crash_handler_inprocess_posix.cpp @@ -163,7 +163,17 @@ static void write_stack_trace(int fd, void* instruction_pointer, void* frame_poi // address points to the instruction to be executed after this function returns // (immediately following the call that created this frame). Symbolication tools // will adjust to resolve the actual call site. +#ifdef __aarch64__ + // Strip pointer authentication codes from the return address: on AArch64, return + // addresses stored on the stack have a PAC signature encoded in their upper 16 + // bits, so we need to mask to 48-bit virtual address space to end up with the + // actual address within the relevant module. + uint64_t raw_ret = reinterpret_cast(ret_addr); + raw_ret &= 0x0000ffffffffffff; + WriteCrashReportStackFrame(fd, raw_ret); +#else WriteCrashReportStackFrame(fd, reinterpret_cast(ret_addr)); +#endif // Reading frame[0] into fp (effectively dereferencing fp) moves to the next frame fp = frame[0];