Implement support for pointer authentication (PAC) in userland. With PAC
authorkettenis <kettenis@openbsd.org>
Sat, 10 Jun 2023 19:30:48 +0000 (19:30 +0000)
committerkettenis <kettenis@openbsd.org>
Sat, 10 Jun 2023 19:30:48 +0000 (19:30 +0000)
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@

13 files changed:
sys/arch/arm64/arm64/cpu.c
sys/arch/arm64/arm64/machdep.c
sys/arch/arm64/arm64/pmap.c
sys/arch/arm64/arm64/process_machdep.c
sys/arch/arm64/arm64/trap.c
sys/arch/arm64/arm64/vm_machdep.c
sys/arch/arm64/include/armreg.h
sys/arch/arm64/include/cpu.h
sys/arch/arm64/include/pmap.h
sys/arch/arm64/include/ptrace.h
sys/kern/exec_elf.c
sys/kern/sys_process.c
sys/sys/exec_elf.h

index 0ada77a..17191ae 100644 (file)
@@ -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 <drahn@dalerahn.com>
@@ -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);
index 20ad76d..1a582fa 100644 (file)
@@ -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 <patrick@blueri.se>
  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
@@ -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));
index 038adcf..533fbe6 100644 (file)
@@ -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 <drahn@dalerahn.com>
  *
@@ -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);
index 26671a7..e57d7f3 100644 (file)
@@ -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 <patrick@blueri.se>
  *
@@ -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);
+}
index d7f0d84..c356163 100644 (file)
@@ -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;
index 0d7a19a..d2e6a9f 100644 (file)
@@ -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);
index 7c5be0c..94e76ff 100644 (file)
@@ -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
 #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 */
 #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
 #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 */
 /*
index 2ddaec5..9ca13c8 100644 (file)
@@ -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 <drahn@dalerahn.com>
  *
  * 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 <machine/intr.h>
 #include <machine/frame.h>
 #include <machine/armreg.h>
index d61d993..8d5bbf2 100644 (file)
@@ -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 <drahn@dalerahn.com>
  *
@@ -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);
index a5ca947..ee208d0 100644 (file)
@@ -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 <patrick@blueri.se>
  *
@@ -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
index f7c11b4..2d228ff 100644 (file)
@@ -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);
index 1fdfc65..9533db1 100644 (file)
@@ -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);
index 80580a0..f77649e 100644 (file)
@@ -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. */