Permalink
Browse files

Implement FUCKWIT for arm64; unmap the kernel almost entirely while u…

…serland

is running.  This provides protection against meltown on cores that are
vilnerable (just Cortex-A75 so far) but also seems to be an essential to
protect against spectre-like attacks against the kernel.

This implementation only exposes a single treampoline page that does not
contain any kernel virtual addresses and also hides the real virtual address
of the exception vectors, which helps on cores vulnerable to "variant 3a"
(Cortex-A57, Cortex-A72).  The implementation is inspired by the work done
by Will Deacon for Linux, but there are no knobs to turn it off.  The
overhead is fairly limited: around 3-4% slowdown on Cortex-A57.

ok patrick@, deraadt@
  • Loading branch information...
kettenis
kettenis committed Jan 10, 2018
1 parent a9d11d8 commit a97a42d9de4f9172faef873359aef60bcf18fd57
@@ -1,4 +1,4 @@
/* $OpenBSD: cpufunc_asm.S,v 1.3 2017/03/24 19:48:01 kettenis Exp $ */
/* $OpenBSD: cpufunc_asm.S,v 1.4 2018/01/10 23:27:18 kettenis Exp $ */
/*-
* Copyright (c) 2014 Robin Randhawa
* Copyright (c) 2015 The FreeBSD Foundation
@@ -79,9 +79,11 @@
*/
ENTRY(cpu_setttb)
dsb ish
msr ttbr0_el1, x0
dsb ish
mrs x2, ttbr1_el1
bfi x2, x0, #48, #16
msr ttbr1_el1, x2
isb
msr ttbr0_el1, x1
isb
ret
END(cpu_setttb)
@@ -1,4 +1,4 @@
/* $OpenBSD: exception.S,v 1.4 2017/08/05 17:30:51 drahn Exp $ */
/* $OpenBSD: exception.S,v 1.5 2018/01/10 23:27:18 kettenis Exp $ */
/*-
* Copyright (c) 2014 Andrew Turner
* All rights reserved.
@@ -155,15 +155,25 @@ handle_el1h_irq:
handle_el1h_error:
brk 0xf13
.macro return
msr tpidrro_el0, x18
ldr x18, =trampoline_vectors
msr vbar_el1, x18
isb
b tramp_return
.endm
.globl handle_el0_sync
handle_el0_sync:
save_registers 0
mov x0, sp
bl do_el0_sync
do_ast
bl _C_LABEL(vfp_enable)
restore_registers 0
eret
return
.globl handle_el0_irq
handle_el0_irq:
save_registers 0
bl _C_LABEL(vfp_save)
@@ -172,8 +182,9 @@ handle_el0_irq:
do_ast
bl _C_LABEL(vfp_enable)
restore_registers 0
eret
return
.globl handle_el0_error
handle_el0_error:
save_registers 0
mov x0, sp
@@ -184,7 +195,7 @@ handle_el0_error:
ENTRY(syscall_return)
do_ast
restore_registers 0
eret
return
.macro vempty
.align 7
@@ -210,13 +221,12 @@ exception_vectors:
vempty /* FIQ EL1h */
vector el1h_error /* Error EL1h */
vector el0_sync /* Synchronous 64-bit EL0 */
vector el0_irq /* IRQ 64-bit EL0 */
vempty /* Synchronous 64-bit EL0 */
vempty /* IRQ 64-bit EL0 */
vempty /* FIQ 64-bit EL0 */
vector el0_error /* Error 64-bit EL0 */
vempty /* Error 64-bit EL0 */
vempty /* Synchronous 32-bit EL0 */
vempty /* IRQ 32-bit EL0 */
vempty /* FIQ 32-bit EL0 */
vempty /* Error 32-bit EL0 */
@@ -1,4 +1,4 @@
/* $OpenBSD: machdep.c,v 1.24 2018/01/04 14:30:08 kettenis Exp $ */
/* $OpenBSD: machdep.c,v 1.25 2018/01/10 23:27:18 kettenis Exp $ */
/*
* Copyright (c) 2014 Patrick Wildt <patrick@blueri.se>
*
@@ -236,13 +236,17 @@ struct trapframe proc0tf;
void
cpu_startup()
{
u_int loop;
paddr_t minaddr;
paddr_t maxaddr;
proc0.p_addr = proc0paddr;
/*
* Give pmap a chance to set up a few more things now the vm
* is initialised
*/
pmap_postinit();
/*
* Initialize error message buffer (at end of core).
@@ -1,4 +1,4 @@
/* $OpenBSD: pmap.c,v 1.42 2018/01/04 14:30:08 kettenis Exp $ */
/* $OpenBSD: pmap.c,v 1.43 2018/01/10 23:27:18 kettenis Exp $ */
/*
* Copyright (c) 2008-2009,2014-2016 Dale Rahn <drahn@dalerahn.com>
*
@@ -37,6 +37,9 @@
void pmap_setttb(struct proc *p);
void pmap_free_asid(pmap_t pm);
/* We run userland code with ASIDs that have the low bit set. */
#define ASID_USER 1
static inline void
ttlb_flush(pmap_t pm, vaddr_t va)
{
@@ -48,10 +51,13 @@ ttlb_flush(pmap_t pm, vaddr_t va)
} else {
resva |= (uint64_t)pm->pm_asid << 48;
cpu_tlb_flush_asid(resva);
resva |= (uint64_t)ASID_USER << 48;
cpu_tlb_flush_asid(resva);
}
}
struct pmap kernel_pmap_;
struct pmap pmap_tramp;
LIST_HEAD(pted_pv_head, pte_desc);
@@ -514,12 +520,9 @@ pmap_enter(pmap_t pm, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
ttlb_flush(pm, va & ~PAGE_MASK);
if (flags & PROT_EXEC) {
if (pg != NULL) {
need_sync = ((pg->pg_flags & PG_PMAP_EXE) == 0);
atomic_setbits_int(&pg->pg_flags, PG_PMAP_EXE);
} else
need_sync = 1;
if (pg != NULL && (flags & PROT_EXEC)) {
need_sync = ((pg->pg_flags & PG_PMAP_EXE) == 0);
atomic_setbits_int(&pg->pg_flags, PG_PMAP_EXE);
}
if (need_sync && (pm == pmap_kernel() || (curproc &&
@@ -1078,12 +1081,17 @@ pmap_bootstrap(long kvo, paddr_t lpt1, long kernelstart, long kernelend,
* via physical pointers
*/
pt1pa = pmap_steal_avail(sizeof(struct pmapvp1), Lx_TABLE_ALIGN, &va);
pt1pa = pmap_steal_avail(2 * sizeof(struct pmapvp1), Lx_TABLE_ALIGN,
&va);
vp1 = (struct pmapvp1 *)pt1pa;
pmap_kernel()->pm_vp.l1 = (struct pmapvp1 *)va;
pmap_kernel()->pm_privileged = 1;
pmap_kernel()->pm_asid = 0;
pmap_tramp.pm_vp.l1 = (struct pmapvp1 *)va + 1;
pmap_tramp.pm_privileged = 1;
pmap_tramp.pm_asid = 0;
/* allocate Lx entries */
for (i = VP_IDX1(VM_MIN_KERNEL_ADDRESS);
i <= VP_IDX1(VM_MAX_KERNEL_ADDRESS);
@@ -1184,7 +1192,7 @@ pmap_bootstrap(long kvo, paddr_t lpt1, long kernelstart, long kernelend,
vp2->l2[VP_IDX2(mapva)] = mappa | L2_BLOCK |
ATTR_IDX(PTE_ATTR_WB) | ATTR_SH(SH_INNER) |
ap_bits_kern[prot];
ATTR_nG | ap_bits_kern[prot];
}
}
@@ -1429,6 +1437,7 @@ pmap_init(void)
tcr = READ_SPECIALREG(tcr_el1);
tcr &= ~TCR_T0SZ(0x3f);
tcr |= TCR_T0SZ(64 - USER_SPACE_BITS);
tcr |= TCR_A1;
WRITE_SPECIALREG(tcr_el1, tcr);
pool_init(&pmap_pmap_pool, sizeof(struct pmap), 0, IPL_NONE, 0,
@@ -1473,7 +1482,7 @@ pmap_pte_update(struct pte_desc *pted, uint64_t *pl3)
{
uint64_t pte, access_bits;
pmap_t pm = pted->pted_pmap;
uint64_t attr = 0;
uint64_t attr = ATTR_nG;
/* see mair in locore.S */
switch (pted->pted_va & PMAP_CACHE_BITS) {
@@ -1504,9 +1513,6 @@ pmap_pte_update(struct pte_desc *pted, uint64_t *pl3)
else
access_bits = ap_bits_user[pted->pted_pte & PROT_MASK];
if (pted->pted_va < VM_MIN_KERNEL_ADDRESS)
access_bits |= ATTR_nG;
pte = (pted->pted_pte & PTE_RPGN) | attr | access_bits | L3_P;
*pl3 = pte;
}
@@ -1655,6 +1661,13 @@ pmap_fault_fixup(pmap_t pm, vaddr_t va, vm_prot_t ftype, int user)
void
pmap_postinit(void)
{
extern char trampoline_vectors[];
paddr_t pa;
memset(pmap_tramp.pm_vp.l1, 0, sizeof(struct pmapvp1));
pmap_extract(pmap_kernel(), (vaddr_t)trampoline_vectors, &pa);
pmap_enter(&pmap_tramp, (vaddr_t)trampoline_vectors, pa,
PROT_READ | PROT_EXEC, PROT_READ | PROT_EXEC | PMAP_WIRED);
}
void
@@ -2040,6 +2053,13 @@ pmap_map_early(paddr_t spa, psize_t len)
}
}
/*
* We allocate ASIDs in pairs. The first ASID is used to run the
* kernel and has both userland and the full kernel mapped. The
* second ASID is used for running userland and has only the
* trampoline page mapped in addition to userland.
*/
#define NUM_ASID (1 << 16)
uint64_t pmap_asid[NUM_ASID / 64];
@@ -2049,32 +2069,34 @@ pmap_allocate_asid(pmap_t pm)
int asid, bit;
do {
asid = arc4random() & (NUM_ASID - 1);
asid = arc4random() & (NUM_ASID - 2);
bit = (asid & (64 - 1));
} while (asid == 0 || (pmap_asid[asid / 64] & (1ULL << bit)));
} while (asid == 0 || (pmap_asid[asid / 64] & (3ULL << bit)));
pmap_asid[asid / 64] |= (1ULL << bit);
pmap_asid[asid / 64] |= (3ULL << bit);
pm->pm_asid = asid;
}
void
pmap_free_asid(pmap_t pm)
{
int asid, bit;
int bit;
KASSERT(pm != curcpu()->ci_curpm);
cpu_tlb_flush_asid_all((uint64_t)pm->pm_asid << 48);
cpu_tlb_flush_asid_all((uint64_t)(pm->pm_asid | ASID_USER) << 48);
asid = pm->pm_asid;
bit = (asid & (64 - 1));
pmap_asid[asid / 64] &= ~(1ULL << bit);
bit = (pm->pm_asid & (64 - 1));
pmap_asid[pm->pm_asid / 64] &= ~(3ULL << bit);
}
void
pmap_setttb(struct proc *p)
{
pmap_t pm = p->p_vmspace->vm_map.pmap;
cpu_setttb(((uint64_t)pm->pm_asid << 48) | pm->pm_pt0pa);
WRITE_SPECIALREG(ttbr0_el1, pmap_kernel()->pm_pt0pa);
__asm volatile("isb");
cpu_setttb(pm->pm_asid, pm->pm_pt0pa);
curcpu()->ci_curpm = pm;
}
Oops, something went wrong.

0 comments on commit a97a42d

Please sign in to comment.