Add support for Privileged Access Never (ARMv8.1-PAN).

PAN provides the same functionality as SMAP on x86: it forbids kernel
access to userland pages when PSTATE.PAN=1, and allows such accesses when
PSTATE.PAN=0.

We clear SCTLR_SPAN, to guarantee that PAN=1 each time the kernel is
entered. We catch PAN faults and panic right away without further
processing. In copyin, copyout, etc, we temporarily authorize access to
userland pages.

PAN is a very useful exploit mitigation. Reviewed by ryo@, thanks. Tested
on Qemu. Enabled by default.
This commit is contained in:
maxv 2020-08-02 06:58:16 +00:00
parent 89feefa3b0
commit cf570c9e4c
13 changed files with 156 additions and 23 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: aarch64_machdep.c,v 1.45 2020/07/16 11:36:35 skrll Exp $ */
/* $NetBSD: aarch64_machdep.c,v 1.46 2020/08/02 06:58:16 maxv Exp $ */
/*-
* Copyright (c) 2014 The NetBSD Foundation, Inc.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(1, "$NetBSD: aarch64_machdep.c,v 1.45 2020/07/16 11:36:35 skrll Exp $");
__KERNEL_RCSID(1, "$NetBSD: aarch64_machdep.c,v 1.46 2020/08/02 06:58:16 maxv Exp $");
#include "opt_arm_debug.h"
#include "opt_cpuoptions.h"
@ -478,6 +478,14 @@ SYSCTL_SETUP(sysctl_machdep_setup, "sysctl machdep subtree setup")
sysctl_machdep_tagged_address, 0, NULL, 0,
CTL_MACHDEP, CTL_CREATE, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_INT, "pan",
SYSCTL_DESCR("Whether Privileged Access Never is enabled"),
NULL, 0,
&aarch64_pan_enabled, 0,
CTL_MACHDEP, CTL_CREATE, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_INT, "pac",

View File

@ -1,4 +1,4 @@
/* $NetBSD: copyinout.S,v 1.10 2020/06/30 16:20:00 maxv Exp $ */
/* $NetBSD: copyinout.S,v 1.11 2020/08/02 06:58:16 maxv Exp $ */
/*-
* Copyright (c) 2014 The NetBSD Foundation, Inc.
@ -33,7 +33,27 @@
#include <aarch64/asm.h>
#include "assym.h"
RCSID("$NetBSD: copyinout.S,v 1.10 2020/06/30 16:20:00 maxv Exp $");
RCSID("$NetBSD: copyinout.S,v 1.11 2020/08/02 06:58:16 maxv Exp $");
#ifdef ARMV81_PAN
#define PAN_ENABLE \
adrl x9, _C_LABEL(aarch64_pan_enabled) ; \
ldr w9, [x9] ; \
cbz w9, 666f ; \
msr pan, #1 ; \
666:
#define PAN_DISABLE \
adrl x9, _C_LABEL(aarch64_pan_enabled) ; \
ldr w9, [x9] ; \
cbz w9, 666f ; \
msr pan, #0 ; \
666:
#else
#define PAN_ENABLE /* nothing */
#define PAN_DISABLE /* nothing */
#endif
ARMV8_DEFINE_OPTIONS
.macro enter_cpu_onfault
stp fp, lr, [sp, #-16]! /* save fp, lr */
@ -55,6 +75,7 @@ RCSID("$NetBSD: copyinout.S,v 1.10 2020/06/30 16:20:00 maxv Exp $");
mov x0, x19 /* x0 = x19 = arg0 */
mov x1, x20 /* x1 = x20 = arg1 */
PAN_DISABLE /* disable PAN */
.endm
.macro exit_cpu_onfault
@ -63,6 +84,7 @@ RCSID("$NetBSD: copyinout.S,v 1.10 2020/06/30 16:20:00 maxv Exp $");
ldr x0, [x0, #CI_CURLWP] /* x0 = curlwp */
str xzr, [x0, #L_MD_ONFAULT] /* lwp->l_md_onfault = NULL */
9:
PAN_ENABLE /* enable PAN */
add sp, sp, #FB_T_SIZE /* pop stack */
ldp x19, x20, [sp], #16 /* restore x19, x20 */
ldp fp, lr, [sp], #16 /* restore fp, lr */

View File

@ -1,4 +1,4 @@
/* $NetBSD: cpufunc.c,v 1.23 2020/07/04 04:59:36 rin Exp $ */
/* $NetBSD: cpufunc.c,v 1.24 2020/08/02 06:58:16 maxv Exp $ */
/*
* Copyright (c) 2017 Ryo Shimizu <ryo@nerv.org>
@ -30,7 +30,7 @@
#include "opt_multiprocessor.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cpufunc.c,v 1.23 2020/07/04 04:59:36 rin Exp $");
__KERNEL_RCSID(0, "$NetBSD: cpufunc.c,v 1.24 2020/08/02 06:58:16 maxv Exp $");
#include <sys/param.h>
#include <sys/types.h>
@ -50,6 +50,7 @@ u_int arm_dcache_maxline;
u_int aarch64_cache_vindexsize;
u_int aarch64_cache_prefer_mask;
int aarch64_pan_enabled __read_mostly;
int aarch64_pac_enabled __read_mostly;
/* cache info per cluster. the same cluster has the same cache configuration? */
@ -474,6 +475,36 @@ set_cpufuncs(void)
return 0;
}
void
aarch64_pan_init(int primary)
{
#ifdef ARMV81_PAN
uint64_t reg, sctlr;
/* CPU0 does the detection. */
if (primary) {
reg = reg_id_aa64mmfr1_el1_read();
if (__SHIFTOUT(reg, ID_AA64MMFR1_EL1_PAN) !=
ID_AA64MMFR1_EL1_PAN_NONE)
aarch64_pan_enabled = 1;
}
if (!aarch64_pan_enabled)
return;
/*
* On an exception to EL1, have the CPU set the PAN bit automatically.
* This ensures PAN is enabled each time the kernel is entered.
*/
sctlr = reg_sctlr_el1_read();
sctlr &= ~SCTLR_SPAN;
reg_sctlr_el1_write(sctlr);
/* Set the PAN bit right now. */
reg_pan_write(1);
#endif
}
/*
* In order to avoid inconsistencies with pointer authentication
* in this function itself, the caller must enable PAC according

View File

@ -1,4 +1,4 @@
/* $NetBSD: db_interface.c,v 1.7 2019/01/27 02:08:36 pgoyette Exp $ */
/* $NetBSD: db_interface.c,v 1.8 2020/08/02 06:58:16 maxv Exp $ */
/*
* Copyright (c) 2017 Ryo Shimizu <ryo@nerv.org>
@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.7 2019/01/27 02:08:36 pgoyette Exp $");
__KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.8 2020/08/02 06:58:16 maxv Exp $");
#include <sys/param.h>
#include <sys/types.h>
@ -67,6 +67,9 @@ db_read_bytes(vaddr_t addr, size_t size, char *data)
}
lastpage = atop((vaddr_t)src);
if (aarch64_pan_enabled)
reg_pan_write(0); /* disable PAN */
tmp = (uintptr_t)src | (uintptr_t)data;
if ((size >= 8) && ((tmp & 7) == 0)) {
*(uint64_t *)data = *(const uint64_t *)src;
@ -87,6 +90,9 @@ db_read_bytes(vaddr_t addr, size_t size, char *data)
*data++ = *src++;
size--;
}
if (aarch64_pan_enabled)
reg_pan_write(1); /* enable PAN */
}
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: fault.c,v 1.14 2020/07/08 03:45:13 ryo Exp $ */
/* $NetBSD: fault.c,v 1.15 2020/08/02 06:58:16 maxv Exp $ */
/*
* Copyright (c) 2017 Ryo Shimizu <ryo@nerv.org>
@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: fault.c,v 1.14 2020/07/08 03:45:13 ryo Exp $");
__KERNEL_RCSID(0, "$NetBSD: fault.c,v 1.15 2020/08/02 06:58:16 maxv Exp $");
#include "opt_compat_netbsd32.h"
#include "opt_ddb.h"
@ -136,6 +136,7 @@ data_abort_handler(struct trapframe *tf, uint32_t eclass)
vm_prot_t ftype;
int error = 0, len;
const bool user = IS_SPSR_USER(tf->tf_spsr) ? true : false;
bool is_pan_trap = false;
bool fatalabort;
const char *faultstr;
@ -191,6 +192,16 @@ data_abort_handler(struct trapframe *tf, uint32_t eclass)
}
#endif
if (__predict_false(!user && (map != kernel_map) &&
(tf->tf_spsr & SPSR_PAN))) {
/*
* We were in kernel mode, faulted on a user address,
* and had PAN enabled. This is a fatal fault.
*/
is_pan_trap = true;
goto handle_fault;
}
/* reference/modified emulation */
if (pmap_fault_fixup(map->pmap, va, ftype, user)) {
UVMHIST_LOG(pmaphist, "fixed: va=%016llx", tf->tf_far, 0, 0, 0);
@ -218,6 +229,7 @@ data_abort_handler(struct trapframe *tf, uint32_t eclass)
return;
}
handle_fault:
fsc = __SHIFTOUT(esr, ESR_ISS_DATAABORT_DFSC); /* also IFSC */
if (user) {
if (!fatalabort) {
@ -326,6 +338,10 @@ data_abort_handler(struct trapframe *tf, uint32_t eclass)
len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
", State 2 Fault");
if (is_pan_trap)
len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
", PAN Set");
len += snprintf(panicinfo + len, sizeof(panicinfo) - len,
": pc %016"PRIxREGISTER, tf->tf_pc);

View File

@ -1,4 +1,4 @@
/* $NetBSD: fusu.S,v 1.6 2019/04/06 03:06:24 thorpej Exp $ */
/* $NetBSD: fusu.S,v 1.7 2020/08/02 06:58:16 maxv Exp $ */
/*-
* Copyright (c) 2014, 2019 The NetBSD Foundation, Inc.
@ -32,7 +32,27 @@
#include <aarch64/asm.h>
#include "assym.h"
RCSID("$NetBSD: fusu.S,v 1.6 2019/04/06 03:06:24 thorpej Exp $");
RCSID("$NetBSD: fusu.S,v 1.7 2020/08/02 06:58:16 maxv Exp $");
#ifdef ARMV81_PAN
#define PAN_ENABLE \
adrl x9, _C_LABEL(aarch64_pan_enabled) ; \
ldr w9, [x9] ; \
cbz w9, 666f ; \
msr pan, #1 ; \
666:
#define PAN_DISABLE \
adrl x9, _C_LABEL(aarch64_pan_enabled) ; \
ldr w9, [x9] ; \
cbz w9, 666f ; \
msr pan, #0 ; \
666:
#else
#define PAN_ENABLE /* nothing */
#define PAN_DISABLE /* nothing */
#endif
ARMV8_DEFINE_OPTIONS
.macro enter_cpu_onfault
stp fp, lr, [sp, #-16]! /* save fp, lr */
@ -47,6 +67,8 @@ RCSID("$NetBSD: fusu.S,v 1.6 2019/04/06 03:06:24 thorpej Exp $");
mov x0, sp /* x0 = faultbuf */
bl cpu_set_onfault /* x0 = cpu_set_onfault() */
cbnz x0, 9f /* return if error */
PAN_DISABLE /* disable PAN */
.endm
.macro exit_cpu_onfault
@ -55,6 +77,7 @@ RCSID("$NetBSD: fusu.S,v 1.6 2019/04/06 03:06:24 thorpej Exp $");
ldr x1, [x1, #CI_CURLWP] /* x1 = curlwp */
str xzr, [x1, #L_MD_ONFAULT] /* lwp->l_md_onfault = NULL */
9:
PAN_ENABLE /* enable PAN */
add sp, sp, #FB_T_SIZE /* pop stack */
ldp x19, x20, [sp], #16 /* restore x19, x20 */
ldp fp, lr, [sp], #16 /* restore fp, lr */

View File

@ -1,4 +1,4 @@
/* $NetBSD: locore.S,v 1.68 2020/07/17 07:16:10 ryo Exp $ */
/* $NetBSD: locore.S,v 1.69 2020/08/02 06:58:16 maxv Exp $ */
/*
* Copyright (c) 2017 Ryo Shimizu <ryo@nerv.org>
@ -38,7 +38,7 @@
#include <aarch64/hypervisor.h>
#include "assym.h"
RCSID("$NetBSD: locore.S,v 1.68 2020/07/17 07:16:10 ryo Exp $")
RCSID("$NetBSD: locore.S,v 1.69 2020/08/02 06:58:16 maxv Exp $")
#ifdef AARCH64_DEVICE_MEM_STRONGLY_ORDERED
#define MAIR_DEVICE_MEM MAIR_DEVICE_nGnRnE
@ -180,6 +180,10 @@ vstart:
msr tpidr_el1, x0 /* curcpu is cpu_info[0] */
DPRINTREG("curcpu = ", x0);
/* init PAN if supported */
mov x0, #1
bl aarch64_pan_init
/* init PAC if supported */
mov x0, #1
bl aarch64_pac_init
@ -515,6 +519,10 @@ mp_vstart:
add x2, x2, #(UPAGES * PAGE_SIZE)
sub sp, x2, #TF_SIZE /* sp = pcb + USPACE - TF_SIZE */
/* init PAN if supported */
mov x0, #0
bl aarch64_pan_init
/* init PAC if supported */
mov x0, #0
bl aarch64_pac_init

View File

@ -1,4 +1,4 @@
/* $NetBSD: trap.c,v 1.35 2020/08/01 02:06:59 riastradh Exp $ */
/* $NetBSD: trap.c,v 1.36 2020/08/02 06:58:16 maxv Exp $ */
/*-
* Copyright (c) 2014 The NetBSD Foundation, Inc.
@ -31,7 +31,7 @@
#include <sys/cdefs.h>
__KERNEL_RCSID(1, "$NetBSD: trap.c,v 1.35 2020/08/01 02:06:59 riastradh Exp $");
__KERNEL_RCSID(1, "$NetBSD: trap.c,v 1.36 2020/08/02 06:58:16 maxv Exp $");
#include "opt_arm_intr_impl.h"
#include "opt_compat_netbsd32.h"
@ -68,6 +68,7 @@ __KERNEL_RCSID(1, "$NetBSD: trap.c,v 1.35 2020/08/01 02:06:59 riastradh Exp $");
#include <aarch64/machdep.h>
#include <aarch64/armreg.h>
#include <aarch64/locore.h>
#include <aarch64/cpufunc.h>
#ifdef KDB
#include <machine/db_machdep.h>
@ -619,6 +620,8 @@ emul_arm_swp(uint32_t insn, struct trapframe *tf)
/* vaddr will always point to userspace, since it has only 32bit */
if ((error = cpu_set_onfault(&fb)) == 0) {
if (aarch64_pan_enabled)
reg_pan_write(0); /* disable PAN */
if (insn & 0x00400000) {
/* swpb */
val = atomic_swap_8((uint8_t *)vaddr, val);
@ -631,6 +634,8 @@ emul_arm_swp(uint32_t insn, struct trapframe *tf)
} else {
tf->tf_far = reg_far_el1_read();
}
if (aarch64_pan_enabled)
reg_pan_write(1); /* enable PAN */
return error;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: armreg.h,v 1.51 2020/08/01 08:47:05 maxv Exp $ */
/* $NetBSD: armreg.h,v 1.52 2020/08/02 06:58:16 maxv Exp $ */
/*-
* Copyright (c) 2014 The NetBSD Foundation, Inc.
@ -602,6 +602,9 @@ AARCH64REG_WRITE_INLINE3(APGAKeyLo_EL1, apgakeylo_el1, ATTR_ARCH("armv8.3-a"))
AARCH64REG_READ_INLINE3(APGAKeyHi_EL1, apgakeyhi_el1, ATTR_ARCH("armv8.3-a"))
AARCH64REG_WRITE_INLINE3(APGAKeyHi_EL1, apgakeyhi_el1, ATTR_ARCH("armv8.3-a"))
AARCH64REG_READ_INLINE3(pan, pan, ATTR_ARCH("armv8.1-a"))
AARCH64REG_WRITE_INLINE3(pan, pan, ATTR_ARCH("armv8.1-a"))
AARCH64REG_READ_INLINE(cpacr_el1) // Coprocessor Access Control Regiser
AARCH64REG_WRITE_INLINE(cpacr_el1)
@ -868,6 +871,7 @@ AARCH64REG_WRITE_INLINE(spsr_el1)
#define SPSR_A32_Q __BIT(27) // A32: Overflow
#define SPSR_A32_IT1 __BIT(26) // A32: IT[1]
#define SPSR_A32_IT0 __BIT(25) // A32: IT[0]
#define SPSR_PAN __BIT(22) // Privileged Access Never
#define SPSR_SS __BIT(21) // Software Step
#define SPSR_SS_SHIFT 21
#define SPSR_IL __BIT(20) // Instruction Length

View File

@ -1,4 +1,4 @@
/* $NetBSD: asm.h,v 1.8 2020/05/11 03:00:57 ryo Exp $ */
/* $NetBSD: asm.h,v 1.9 2020/08/02 06:58:16 maxv Exp $ */
#ifndef _AARCH64_ASM_H_
#define _AARCH64_ASM_H_
@ -33,12 +33,16 @@
* ARMv8 options to be made available for the compiler to use. Should be
* inserted at the beginning of the ASM files that need them.
*
* For now the only option is PAC, needed for the compiler to recognize
* the key registers.
* The options are:
* - PAN, needed for the compiler to recognize the PAN register.
* - PAC, needed for the compiler to recognize the key registers.
*/
#ifdef ARMV83_PAC
#define ARMV8_DEFINE_OPTIONS \
.arch armv8.3-a
#elif defined(ARMV81_PAN)
#define ARMV8_DEFINE_OPTIONS \
.arch armv8.1-a
#else
#define ARMV8_DEFINE_OPTIONS /* nothing */
#endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: cpufunc.h,v 1.16 2020/07/01 07:59:16 ryo Exp $ */
/* $NetBSD: cpufunc.h,v 1.17 2020/08/02 06:58:16 maxv Exp $ */
/*
* Copyright (c) 2017 Ryo Shimizu <ryo@nerv.org>
@ -63,8 +63,10 @@ extern u_int aarch64_cache_vindexsize; /* cachesize/way (VIVT/VIPT) */
extern u_int aarch64_cache_prefer_mask;
extern u_int cputype; /* compat arm */
extern int aarch64_pan_enabled;
extern int aarch64_pac_enabled;
void aarch64_pan_init(int);
int aarch64_pac_init(int);
int set_cpufuncs(void);

View File

@ -1,4 +1,4 @@
# $NetBSD: files.arm,v 1.160 2020/07/27 20:51:29 riastradh Exp $
# $NetBSD: files.arm,v 1.161 2020/08/02 06:58:16 maxv Exp $
# temporary define to allow easy moving to ../arch/arm/arm32
defflag ARM32
@ -79,6 +79,7 @@ defflag opt_arm_debug.h VERBOSE_INIT_ARM
defparam opt_arm_debug.h EARLYCONS
# ARMv8-specific options
defflag opt_cpuoptions.h ARMV81_PAN
defflag opt_cpuoptions.h ARMV83_PAC
defflag opt_cpuoptions.h ARMV85_BTI

View File

@ -1,5 +1,5 @@
#
# $NetBSD: GENERIC64,v 1.160 2020/07/16 11:36:35 skrll Exp $
# $NetBSD: GENERIC64,v 1.161 2020/08/02 06:58:16 maxv Exp $
#
# GENERIC ARM (aarch64) kernel
#
@ -46,6 +46,9 @@ options INCLUDE_CONFIG_FILE
#options EARLYCONS=thunderx, CONSADDR=0x87e024000000
#options EARLYCONS=virt, CONSADDR=0x09000000
# Privileged Access Never (PAN).
options ARMV81_PAN
# Pointer Authentication (PAC).
#makeoptions ARMV83_PAC=1
#options ARMV83_PAC