[aarch64] Enable pointer authentication support for aarch64 bare metal/kernel mode addresses

At the moment GDB only handles pointer authentication (pauth) for userspace
addresses and if we're debugging a Linux-hosted program.

The Linux Kernel can be configured to use pauth instructions for some
additional security hardening, but GDB doesn't handle this well.

To overcome this limitation, GDB needs a couple things:

1 - The target needs to advertise pauth support.
2 - The hook to remove non-address bits from a pointer needs to be registered
    in aarch64-tdep.c as opposed to aarch64-linux-tdep.c.

There is a patch for QEMU that addresses the first point, and it makes
QEMU's gdbstub expose a couple more pauth mask registers, so overall we will
have up to 4 pauth masks (2 masks or 4 masks):

pauth_dmask
pauth_cmask
pauth_dmask_high
pauth_cmask_high

pauth_dmask and pauth_cmask are the masks used to remove pauth signatures
from userspace addresses. pauth_dmask_high and pauth_cmask_high masks are used
to remove pauth signatures from kernel addresses.

The second point is easily addressed by moving code around.

When debugging a Linux Kernel built with pauth with an unpatched GDB, we get
the following backtrace:

 #0  __fput (file=0xffff0000c17a6400) at /repos/linux/fs/file_table.c:296
 #1  0xffff8000082bd1f0 in ____fput (work=<optimized out>) at /repos/linux/fs/file_table.c:348
 #2  0x30008000080ade30 [PAC] in ?? ()
 #3  0x30d48000080ade30 in ?? ()
 Backtrace stopped: previous frame identical to this frame (corrupt stack?)

With a patched GDB, we get something a lot more meaningful:

 #0  __fput (file=0xffff0000c1bcfa00) at /repos/linux/fs/file_table.c:296
 #1  0xffff8000082bd1f0 in ____fput (work=<optimized out>) at /repos/linux/fs/file_table.c:348
 #2  0xffff8000080ade30 [PAC] in task_work_run () at /repos/linux/kernel/task_work.c:179
 #3  0xffff80000801db90 [PAC] in resume_user_mode_work (regs=0xffff80000a96beb0) at /repos/linux/include/linux/resume_user_mode.h:49
 #4  do_notify_resume (regs=regs@entry=0xffff80000a96beb0, thread_flags=4) at /repos/linux/arch/arm64/kernel/signal.c:1127
 #5  0xffff800008fb9974 [PAC] in prepare_exit_to_user_mode (regs=0xffff80000a96beb0) at /repos/linux/arch/arm64/kernel/entry-common.c:137
 #6  exit_to_user_mode (regs=0xffff80000a96beb0) at /repos/linux/arch/arm64/kernel/entry-common.c:142
 #7  el0_svc (regs=0xffff80000a96beb0) at /repos/linux/arch/arm64/kernel/entry-common.c:638
 #8  0xffff800008fb9d34 [PAC] in el0t_64_sync_handler (regs=<optimized out>) at /repos/linux/arch/arm64/kernel/entry-common.c:655
 #9  0xffff800008011548 [PAC] in el0t_64_sync () at /repos/linux/arch/arm64/kernel/entry.S:586
 Backtrace stopped: Cannot access memory at address 0xffff80000a96c0c8
This commit is contained in:
Luis Machado
2022-09-11 20:47:18 +01:00
parent fd7f7424f5
commit 6d0020873d
4 changed files with 100 additions and 51 deletions

View File

@@ -1980,40 +1980,6 @@ aarch64_linux_decode_memtag_section (struct gdbarch *gdbarch,
return tags;
}
/* AArch64 implementation of the remove_non_address_bits gdbarch hook. Remove
non address bits from a pointer value. */
static CORE_ADDR
aarch64_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer)
{
aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
/* By default, we assume TBI and discard the top 8 bits plus the VA range
select bit (55). */
CORE_ADDR mask = AARCH64_TOP_BITS_MASK;
if (tdep->has_pauth ())
{
/* Fetch the PAC masks. These masks are per-process, so we can just
fetch data from whatever thread we have at the moment.
Also, we have both a code mask and a data mask. For now they are the
same, but this may change in the future. */
struct regcache *regs = get_current_regcache ();
CORE_ADDR cmask, dmask;
if (regs->cooked_read (tdep->pauth_reg_base, &dmask) != REG_VALID)
dmask = mask;
if (regs->cooked_read (tdep->pauth_reg_base + 1, &cmask) != REG_VALID)
cmask = mask;
mask |= aarch64_mask_from_pac_registers (cmask, dmask);
}
return aarch64_remove_top_bits (pointer, mask);
}
static void
aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
@@ -2066,12 +2032,6 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Syscall record. */
tdep->aarch64_syscall_record = aarch64_linux_syscall_record;
/* The top byte of a user space address known as the "tag",
is ignored by the kernel and can be regarded as additional
data associated with the address. */
set_gdbarch_remove_non_address_bits (gdbarch,
aarch64_remove_non_address_bits);
/* MTE-specific settings and hooks. */
if (tdep->has_mte ())
{