From 4171e492c627a295b38565e1f04e0b7ba62913f8 Mon Sep 17 00:00:00 2001 From: kettenis Date: Sat, 10 Jun 2023 19:30:48 +0000 Subject: [PATCH] Implement support for pointer authentication (PAC) in userland. With PAC it is possible to "sign" pointers with a hidden key. The signature is placed in unused bits of the pointer and can be checked later. This can be used to provide "tail CFI" that is similar to what retguard provides. Debuggers need to be aware of the fact that pointers can be signed. For this purpose a new PT_PACMASK ptrace(2) request is introduced that returns as mask that indicates the bits used for the signature. Separate masks are provided for code and data pointers even though the masks are identical in the current implementation. These masks are also written into a special note section in the core dump. ok patrick@ --- sys/arch/arm64/arm64/cpu.c | 13 ++++++++- sys/arch/arm64/arm64/machdep.c | 19 +++++++------ sys/arch/arm64/arm64/pmap.c | 37 +++++++++++++++++++++++++- sys/arch/arm64/arm64/process_machdep.c | 8 +++++- sys/arch/arm64/arm64/trap.c | 9 ++++++- sys/arch/arm64/arm64/vm_machdep.c | 10 ++++++- sys/arch/arm64/include/armreg.h | 7 ++++- sys/arch/arm64/include/cpu.h | 7 ++++- sys/arch/arm64/include/pmap.h | 9 ++++++- sys/arch/arm64/include/ptrace.h | 7 ++++- sys/kern/exec_elf.c | 23 +++++++++++++++- sys/kern/sys_process.c | 15 ++++++++++- sys/sys/exec_elf.h | 3 ++- 13 files changed, 147 insertions(+), 20 deletions(-) diff --git a/sys/arch/arm64/arm64/cpu.c b/sys/arch/arm64/arm64/cpu.c index 0ada77aad66..17191ae7012 100644 --- a/sys/arch/arm64/arm64/cpu.c +++ b/sys/arch/arm64/arm64/cpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.c,v 1.92 2023/05/30 08:30:00 jsg Exp $ */ +/* $OpenBSD: cpu.c,v 1.93 2023/06/10 19:30:48 kettenis Exp $ */ /* * Copyright (c) 2016 Dale Rahn @@ -944,6 +944,7 @@ cpu_init(void) { uint64_t id_aa64mmfr1, sctlr; uint64_t id_aa64pfr0; + uint64_t id_aa64isar1; uint64_t tcr; WRITE_SPECIALREG(ttbr0_el1, pmap_kernel()->pm_pt0pa); @@ -968,6 +969,16 @@ cpu_init(void) if (ID_AA64PFR0_DIT(id_aa64pfr0) >= ID_AA64PFR0_DIT_IMPL) __asm volatile (".arch armv8.4-a; msr dit, #1"); + /* Enable PAuth. */ + id_aa64isar1 = READ_SPECIALREG(id_aa64isar1_el1); + if (ID_AA64ISAR1_API(id_aa64isar1) >= ID_AA64ISAR1_API_BASE || + ID_AA64ISAR1_APA(id_aa64isar1) >= ID_AA64ISAR1_APA_BASE) { + sctlr = READ_SPECIALREG(sctlr_el1); + sctlr |= SCTLR_EnIA | SCTLR_EnDA; + sctlr |= SCTLR_EnIB | SCTLR_EnDB; + WRITE_SPECIALREG(sctlr_el1, sctlr); + } + /* Initialize debug registers. */ WRITE_SPECIALREG(mdscr_el1, DBG_MDSCR_TDCC); WRITE_SPECIALREG(oslar_el1, 0); diff --git a/sys/arch/arm64/arm64/machdep.c b/sys/arch/arm64/arm64/machdep.c index 20ad76d57c6..1a582fa2727 100644 --- a/sys/arch/arm64/arm64/machdep.c +++ b/sys/arch/arm64/arm64/machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: machdep.c,v 1.81 2023/04/24 10:22:48 kettenis Exp $ */ +/* $OpenBSD: machdep.c,v 1.82 2023/06/10 19:30:48 kettenis Exp $ */ /* * Copyright (c) 2014 Patrick Wildt * Copyright (c) 2021 Mark Kettenis @@ -314,11 +314,6 @@ cpu_switchto(struct proc *old, struct proc *new) cpu_switchto_asm(old, new); } -extern uint64_t cpu_id_aa64isar0; -extern uint64_t cpu_id_aa64isar1; -extern uint64_t cpu_id_aa64pfr0; -extern uint64_t cpu_id_aa64pfr1; - /* * machine dependent system variables. */ @@ -451,13 +446,21 @@ void setregs(struct proc *p, struct exec_package *pack, u_long stack, struct ps_strings *arginfo) { + struct pmap *pm = p->p_vmspace->vm_map.pmap; struct pcb *pcb = &p->p_addr->u_pcb; struct trapframe *tf = pcb->pcb_tf; if (pack->ep_flags & EXEC_NOBTCFI) - p->p_vmspace->vm_map.pmap->pm_guarded = 0; + pm->pm_guarded = 0; else - p->p_vmspace->vm_map.pmap->pm_guarded = ATTR_GP; + pm->pm_guarded = ATTR_GP; + + arc4random_buf(&pm->pm_apiakey, sizeof(pm->pm_apiakey)); + arc4random_buf(&pm->pm_apdakey, sizeof(pm->pm_apdakey)); + arc4random_buf(&pm->pm_apibkey, sizeof(pm->pm_apibkey)); + arc4random_buf(&pm->pm_apdbkey, sizeof(pm->pm_apdbkey)); + arc4random_buf(&pm->pm_apgakey, sizeof(pm->pm_apgakey)); + pmap_setpauthkeys(pm); /* If we were using the FPU, forget about it. */ memset(&pcb->pcb_fpstate, 0, sizeof(pcb->pcb_fpstate)); diff --git a/sys/arch/arm64/arm64/pmap.c b/sys/arch/arm64/arm64/pmap.c index 038adcfca31..533fbe67c26 100644 --- a/sys/arch/arm64/arm64/pmap.c +++ b/sys/arch/arm64/arm64/pmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.c,v 1.96 2023/04/16 11:14:26 kettenis Exp $ */ +/* $OpenBSD: pmap.c,v 1.97 2023/06/10 19:30:48 kettenis Exp $ */ /* * Copyright (c) 2008-2009,2014-2016 Dale Rahn * @@ -2228,6 +2228,38 @@ pmap_show_mapping(uint64_t va) pted, vp3->l3[VP_IDX3(va)], VP_IDX3(va)*8); } +void +pmap_setpauthkeys(struct pmap *pm) +{ + if (ID_AA64ISAR1_API(cpu_id_aa64isar1) >= ID_AA64ISAR1_API_BASE || + ID_AA64ISAR1_APA(cpu_id_aa64isar1) >= ID_AA64ISAR1_APA_BASE) { + __asm volatile (".arch armv8.3-a; msr apiakeylo_el1, %0" + :: "r"(pm->pm_apiakey[0])); + __asm volatile (".arch armv8.3-a; msr apiakeyhi_el1, %0" + :: "r"(pm->pm_apiakey[1])); + __asm volatile (".arch armv8.3-a; msr apdakeylo_el1, %0" + :: "r"(pm->pm_apdakey[0])); + __asm volatile (".arch armv8.3-a; msr apdakeyhi_el1, %0" + :: "r"(pm->pm_apdakey[1])); + __asm volatile (".arch armv8.3-a; msr apibkeylo_el1, %0" + :: "r"(pm->pm_apibkey[0])); + __asm volatile (".arch armv8.3-a; msr apibkeyhi_el1, %0" + :: "r"(pm->pm_apibkey[1])); + __asm volatile (".arch armv8.3-a; msr apdbkeylo_el1, %0" + :: "r"(pm->pm_apdbkey[0])); + __asm volatile (".arch armv8.3-a; msr apdbkeyhi_el1, %0" + :: "r"(pm->pm_apdbkey[1])); + } + + if (ID_AA64ISAR1_GPI(cpu_id_aa64isar1) >= ID_AA64ISAR1_GPI_IMPL || + ID_AA64ISAR1_GPA(cpu_id_aa64isar1) >= ID_AA64ISAR1_GPA_IMPL) { + __asm volatile (".arch armv8.3-a; msr apgakeylo_el1, %0" + :: "r"(pm->pm_apgakey[0])); + __asm volatile (".arch armv8.3-a; msr apgakeyhi_el1, %0" + :: "r"(pm->pm_apgakey[1])); + } +} + void pmap_setttb(struct proc *p) { @@ -2244,6 +2276,9 @@ pmap_setttb(struct proc *p) (pm->pm_asid & ~PMAP_ASID_MASK) != pmap_asid_gen) pmap_allocate_asid(pm); + if (pm != pmap_kernel()) + pmap_setpauthkeys(pm); + WRITE_SPECIALREG(ttbr0_el1, pmap_kernel()->pm_pt0pa); __asm volatile("isb"); cpu_setttb(pm->pm_asid, pm->pm_pt0pa); diff --git a/sys/arch/arm64/arm64/process_machdep.c b/sys/arch/arm64/arm64/process_machdep.c index 26671a70093..e57d7f37188 100644 --- a/sys/arch/arm64/arm64/process_machdep.c +++ b/sys/arch/arm64/arm64/process_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: process_machdep.c,v 1.7 2023/04/16 10:14:59 kettenis Exp $ */ +/* $OpenBSD: process_machdep.c,v 1.8 2023/06/10 19:30:48 kettenis Exp $ */ /* * Copyright (c) 2014 Patrick Wildt * @@ -136,3 +136,9 @@ process_set_pc(struct proc *p, caddr_t addr) } #endif /* PTRACE */ + +register_t +process_get_pacmask(struct proc *p) +{ + return (-1ULL << USER_SPACE_BITS); +} diff --git a/sys/arch/arm64/arm64/trap.c b/sys/arch/arm64/arm64/trap.c index d7f0d84a9fd..c356163e1bf 100644 --- a/sys/arch/arm64/arm64/trap.c +++ b/sys/arch/arm64/arm64/trap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: trap.c,v 1.45 2023/05/15 15:02:06 kettenis Exp $ */ +/* $OpenBSD: trap.c,v 1.46 2023/06/10 19:30:48 kettenis Exp $ */ /*- * Copyright (c) 2014 Andrew Turner * All rights reserved. @@ -220,6 +220,8 @@ do_el1h_sync(struct trapframe *frame) panic("FP exception in the kernel"); case EXCP_BRANCH_TGT: panic("Branch target exception in the kernel"); + case EXCP_FPAC: + panic("Faulting PAC trap in kernel"); case EXCP_INSN_ABORT: kdata_abort(frame, esr, far, 1); break; @@ -286,6 +288,11 @@ do_el0_sync(struct trapframe *frame) sv.sival_ptr = (void *)frame->tf_elr; trapsignal(p, SIGILL, esr, ILL_ILLOPC, sv); break; + case EXCP_FPAC: + curcpu()->ci_flush_bp(); + sv.sival_ptr = (void *)frame->tf_elr; + trapsignal(p, SIGILL, esr, ILL_ILLOPC, sv); + break; case EXCP_SVC: svc_handler(frame); break; diff --git a/sys/arch/arm64/arm64/vm_machdep.c b/sys/arch/arm64/arm64/vm_machdep.c index 0d7a19a3254..d2e6a9f1acf 100644 --- a/sys/arch/arm64/arm64/vm_machdep.c +++ b/sys/arch/arm64/arm64/vm_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm_machdep.c,v 1.11 2023/04/11 00:45:07 jsg Exp $ */ +/* $OpenBSD: vm_machdep.c,v 1.12 2023/06/10 19:30:48 kettenis Exp $ */ /* $NetBSD: vm_machdep.c,v 1.1 2003/04/26 18:39:33 fvdl Exp $ */ /*- @@ -69,11 +69,19 @@ void cpu_fork(struct proc *p1, struct proc *p2, void *stack, void *tcb, void (*func)(void *), void *arg) { + struct pmap *pm = p2->p_vmspace->vm_map.pmap; + struct pmap *pm1 = p1->p_vmspace->vm_map.pmap; struct pcb *pcb = &p2->p_addr->u_pcb; struct pcb *pcb1 = &p1->p_addr->u_pcb; struct trapframe *tf; struct switchframe *sf; + memcpy(pm->pm_apiakey, pm1->pm_apiakey, sizeof(pm->pm_apiakey)); + memcpy(pm->pm_apdakey, pm1->pm_apdakey, sizeof(pm->pm_apdakey)); + memcpy(pm->pm_apibkey, pm1->pm_apibkey, sizeof(pm->pm_apibkey)); + memcpy(pm->pm_apdbkey, pm1->pm_apdbkey, sizeof(pm->pm_apdbkey)); + memcpy(pm->pm_apgakey, pm1->pm_apgakey, sizeof(pm->pm_apgakey)); + /* Save FPU state to PCB if necessary. */ if (pcb1->pcb_flags & PCB_FPU) fpu_save(p1); diff --git a/sys/arch/arm64/include/armreg.h b/sys/arch/arm64/include/armreg.h index 7c5be0c6da9..94e76ff28de 100644 --- a/sys/arch/arm64/include/armreg.h +++ b/sys/arch/arm64/include/armreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: armreg.h,v 1.28 2023/04/16 10:14:59 kettenis Exp $ */ +/* $OpenBSD: armreg.h,v 1.29 2023/06/10 19:30:48 kettenis Exp $ */ /*- * Copyright (c) 2013, 2014 Andrew Turner * Copyright (c) 2015 The FreeBSD Foundation @@ -169,6 +169,7 @@ #define EXCP_ILL_STATE 0x0e /* Illegal execution state */ #define EXCP_SVC 0x15 /* SVC trap */ #define EXCP_MSR 0x18 /* MSR/MRS trap */ +#define EXCP_FPAC 0x1c /* Faulting PAC trap */ #define EXCP_INSN_ABORT_L 0x20 /* Instruction abort, from lower EL */ #define EXCP_INSN_ABORT 0x21 /* Instruction abort, from same EL */ #define EXCP_PC_ALIGN 0x22 /* PC alignment fault */ @@ -642,6 +643,7 @@ #define SCTLR_SED 0x0000000000000100 #define SCTLR_UMA 0x0000000000000200 #define SCTLR_I 0x0000000000001000 +#define SCTLR_EnDB 0x0000000000002000 #define SCTLR_DZE 0x0000000000004000 #define SCTLR_UCT 0x0000000000008000 #define SCTLR_nTWI 0x0000000000010000 @@ -651,6 +653,9 @@ #define SCTLR_EOE 0x0000000001000000 #define SCTLR_EE 0x0000000002000000 #define SCTLR_UCI 0x0000000004000000 +#define SCTLR_EnDA 0x0000000008000000 +#define SCTLR_EnIB 0x0000000040000000 +#define SCTLR_EnIA 0x0000000080000000 /* SPSR_EL1 */ /* diff --git a/sys/arch/arm64/include/cpu.h b/sys/arch/arm64/include/cpu.h index 2ddaec58f66..9ca13c88223 100644 --- a/sys/arch/arm64/include/cpu.h +++ b/sys/arch/arm64/include/cpu.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cpu.h,v 1.35 2023/02/19 17:16:13 kettenis Exp $ */ +/* $OpenBSD: cpu.h,v 1.36 2023/06/10 19:30:48 kettenis Exp $ */ /* * Copyright (c) 2016 Dale Rahn * @@ -61,6 +61,11 @@ * Kernel-only definitions */ +extern uint64_t cpu_id_aa64isar0; +extern uint64_t cpu_id_aa64isar1; +extern uint64_t cpu_id_aa64pfr0; +extern uint64_t cpu_id_aa64pfr1; + #include #include #include diff --git a/sys/arch/arm64/include/pmap.h b/sys/arch/arm64/include/pmap.h index d61d9936c66..8d5bbf2eaa9 100644 --- a/sys/arch/arm64/include/pmap.h +++ b/sys/arch/arm64/include/pmap.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.h,v 1.23 2023/04/16 11:14:26 kettenis Exp $ */ +/* $OpenBSD: pmap.h,v 1.24 2023/06/10 19:30:48 kettenis Exp $ */ /* * Copyright (c) 2008,2009,2014 Dale Rahn * @@ -71,6 +71,11 @@ struct pmap { int pm_privileged; int pm_refs; /* ref count */ struct pmap_statistics pm_stats; /* pmap statistics */ + uint64_t pm_apiakey[2]; + uint64_t pm_apdakey[2]; + uint64_t pm_apibkey[2]; + uint64_t pm_apdbkey[2]; + uint64_t pm_apgakey[2]; }; #define PMAP_PA_MASK ~((paddr_t)PAGE_MASK) /* to remove the flags */ @@ -100,6 +105,8 @@ void pmap_kenter_cache(vaddr_t va, paddr_t pa, vm_prot_t prot, int cacheable); void pmap_page_ro(pmap_t pm, vaddr_t va, vm_prot_t prot); void pmap_page_rw(pmap_t pm, vaddr_t va); +void pmap_setpauthkeys(struct pmap *); + paddr_t pmap_steal_avail(size_t size, int align, void **kva); void pmap_avail_fixup(void); void pmap_physload_avail(void); diff --git a/sys/arch/arm64/include/ptrace.h b/sys/arch/arm64/include/ptrace.h index a5ca9476618..ee208d06c8e 100644 --- a/sys/arch/arm64/include/ptrace.h +++ b/sys/arch/arm64/include/ptrace.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ptrace.h,v 1.1 2016/12/17 23:38:33 patrick Exp $ */ +/* $OpenBSD: ptrace.h,v 1.2 2023/06/10 19:30:48 kettenis Exp $ */ /* * Copyright (c) 2014 Patrick Wildt * @@ -20,3 +20,8 @@ #define PT_SETREGS (PT_FIRSTMACH + 2) #define PT_GETFPREGS (PT_FIRSTMACH + 3) #define PT_SETFPREGS (PT_FIRSTMACH + 4) +#define PT_PACMASK (PT_FIRSTMACH + 5) + +#ifdef _KERNEL +register_t process_get_pacmask(struct proc *p); +#endif diff --git a/sys/kern/exec_elf.c b/sys/kern/exec_elf.c index f7c11b4ccc8..2d228ffe187 100644 --- a/sys/kern/exec_elf.c +++ b/sys/kern/exec_elf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec_elf.c,v 1.181 2023/04/19 15:37:36 kettenis Exp $ */ +/* $OpenBSD: exec_elf.c,v 1.182 2023/06/10 19:30:48 kettenis Exp $ */ /* * Copyright (c) 1996 Per Fogelstrom @@ -1383,6 +1383,9 @@ coredump_note_elf(struct proc *p, void *iocookie, size_t *sizep) #ifdef PT_GETFPREGS struct fpreg freg; #endif +#ifdef PT_PACMASK + register_t pacmask[2]; +#endif size = 0; @@ -1427,6 +1430,24 @@ coredump_note_elf(struct proc *p, void *iocookie, size_t *sizep) size += notesize; #endif +#ifdef PT_PACMASK + notesize = sizeof(nhdr) + elfround(namesize) + + elfround(sizeof(pacmask)); + if (iocookie) { + pacmask[0] = pacmask[1] = process_get_pacmask(p); + + nhdr.namesz = namesize; + nhdr.descsz = sizeof(pacmask); + nhdr.type = NT_OPENBSD_PACMASK; + + error = coredump_writenote_elf(p, iocookie, &nhdr, + name, &pacmask); + if (error) + return (error); + } + size += notesize; +#endif + *sizep = size; /* XXX Add hook for machdep per-LWP notes. */ return (0); diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 1fdfc652366..9533db17572 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sys_process.c,v 1.93 2023/01/24 00:12:03 deraadt Exp $ */ +/* $OpenBSD: sys_process.c,v 1.94 2023/06/10 19:30:48 kettenis Exp $ */ /* $NetBSD: sys_process.c,v 1.55 1996/05/15 06:17:47 tls Exp $ */ /*- @@ -104,6 +104,7 @@ sys_ptrace(struct proc *p, void *v, register_t *retval) struct ptrace_event u_pe; struct ptrace_state u_ps; register_t u_wcookie; + register_t u_pacmask[2]; } u; int size = 0; enum { NONE, IN, IN_ALLOC, OUT, OUT_ALLOC, IN_OUT } mode; @@ -199,6 +200,12 @@ sys_ptrace(struct proc *p, void *v, register_t *retval) size = sizeof u.u_wcookie; data = size; /* suppress the data == size check */ break; +#endif +#ifdef PT_PACMASK + case PT_PACMASK: + mode = OUT; + size = sizeof u.u_pacmask; + break; #endif default: return EINVAL; @@ -731,6 +738,12 @@ ptrace_ustate(struct proc *p, int req, pid_t pid, void *addr, int data, case PT_WCOOKIE: *(register_t *)addr = process_get_wcookie(t); return 0; +#endif +#ifdef PT_PACMASK + case PT_PACMASK: + ((register_t *)addr)[0] = process_get_pacmask(t); + ((register_t *)addr)[1] = process_get_pacmask(t); + return 0; #endif default: KASSERTMSG(0, "%s: unhandled request %d", __func__, req); diff --git a/sys/sys/exec_elf.h b/sys/sys/exec_elf.h index 80580a0466b..f77649efd9e 100644 --- a/sys/sys/exec_elf.h +++ b/sys/sys/exec_elf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: exec_elf.h,v 1.100 2023/04/19 15:37:36 kettenis Exp $ */ +/* $OpenBSD: exec_elf.h,v 1.101 2023/06/10 19:30:48 kettenis Exp $ */ /* * Copyright (c) 1995, 1996 Erik Theisen. All rights reserved. * @@ -666,6 +666,7 @@ typedef struct { #define NT_OPENBSD_FPREGS 21 #define NT_OPENBSD_XFPREGS 22 #define NT_OPENBSD_WCOOKIE 23 +#define NT_OPENBSD_PACMASK 24 struct elfcore_procinfo { /* Version 1 fields start here. */ -- 2.20.1