ref: a8fff045a78bb129a862c2903278479334ac2423
parent: 161f81c60e0d9be150a5675b862c3eb6222f04f0
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Thu Aug 29 03:35:22 EDT 2019
pc64: map kernel text readonly and everything else no-execute the idea is to catch bugs and make kernel exploitation harder by mapping the kernel text section readonly and everything else no-execute. l.s maps the KZERO address space using 2MB pages so to get the 4K granularity for the text section we use the new ptesplit() function to split that mapping up. we need to set EFER no-execute enable bit early in apbootstrap so secondary application processors will understand the NX bit in our shared kernel page tables. also APBOOTSTRAP needs to be kept executable. rebootjump() needs to mark REBOOTADDR page executable.
--- a/sys/src/9/pc64/apbootstrap.s
+++ b/sys/src/9/pc64/apbootstrap.s
@@ -42,6 +42,9 @@
QUAD $0
TEXT _apmach(SB), 1, $-4 /* address APBOOTSTRAP+0x20 */
QUAD $0
+TEXT _apefer(SB), 1, $-4
+ QUAD $0x100 /* Long Mode Enable */
+
TEXT _apbootstrap(SB), 1, $-4
MOVW CS, AX
MOVW AX, DS /* initialise DS */
@@ -85,7 +88,7 @@
MOVL $0xc0000080, CX /* Extended Feature Enable */
RDMSR
- ORL $0x00000100, AX /* Long Mode Enable */
+ ORL _apefer-KZERO(SB), AX
WRMSR
MOVL CR0, DX
--- a/sys/src/9/pc64/main.c
+++ b/sys/src/9/pc64/main.c
@@ -301,6 +301,7 @@
rebootjump(uintptr entry, uintptr code, ulong size)
{void (*f)(uintptr, uintptr, ulong);
+ uintptr *pte;
splhi();
arch->introff();
@@ -310,6 +311,12 @@
*/
*mmuwalk(m->pml4, 0, 3, 0) = *mmuwalk(m->pml4, KZERO, 3, 0);
*mmuwalk(m->pml4, 0, 2, 0) = *mmuwalk(m->pml4, KZERO, 2, 0);
+
+ if((pte = mmuwalk(m->pml4, REBOOTADDR, 1, 0)) != nil)
+ *pte &= ~PTENOEXEC;
+ if((pte = mmuwalk(m->pml4, REBOOTADDR, 0, 0)) != nil)
+ *pte &= ~PTENOEXEC;
+
mmuflushtlb();
/* setup reboot trampoline function */
--- a/sys/src/9/pc64/memory.c
+++ b/sys/src/9/pc64/memory.c
@@ -221,6 +221,8 @@
{uintptr m;
+ if(conf.mem[0].npage != 0)
+ return xspanalloc(BY2PG, BY2PG, 0);
m = mapalloc(&rmapram, 0, BY2PG, BY2PG);
if(m == 0)
return nil;
@@ -543,11 +545,11 @@
switch(type){case MemRAM:
mapfree(&rmapram, base, len);
- flags = PTEWRITE|PTEVALID;
+ flags = PTEWRITE|PTENOEXEC|PTEVALID;
break;
case MemUMB:
mapfree(&rmapumb, base, len);
- flags = PTEWRITE|PTEUNCACHED|PTEVALID;
+ flags = PTEWRITE|PTEUNCACHED|PTENOEXEC|PTEVALID;
break;
case MemUPA:
mapfree(&rmapupa, base, len);
--- a/sys/src/9/pc64/mmu.c
+++ b/sys/src/9/pc64/mmu.c
@@ -73,6 +73,8 @@
mmuflushtlb();
}
+static void kernelro(void);
+
void
mmuinit(void)
{@@ -84,6 +86,9 @@
m->pml4[512] = 0;
m->pml4[0] = 0;
+ if(m->machno == 0)
+ kernelro();
+
m->tss = mallocz(sizeof(Tss), 1);
if(m->tss == nil)
panic("mmuinit: no memory for Tss");@@ -244,8 +249,6 @@
up->kmapcount++;
}
page = p->page;
- } else if(conf.mem[0].npage != 0) {- page = mallocalign(PTSZ, BY2PG, 0, 0);
} else {page = rampage();
}
@@ -285,6 +288,59 @@
ptecount(uintptr va, int level)
{return (1<<PTSHIFT) - (va & PGLSZ(level+1)-1) / PGLSZ(level);
+}
+
+static void
+ptesplit(uintptr* table, uintptr va)
+{+ uintptr *pte, pa, off;
+
+ pte = mmuwalk(table, va, 1, 0);
+ if(pte == nil || (*pte & PTESIZE) == 0 || (va & PGLSZ(1)-1) == 0)
+ return;
+ table = rampage();
+ if(table == nil)
+ panic("ptesplit: out of memory\n");+ va &= -PGLSZ(1);
+ pa = *pte & ~PTESIZE;
+ for(off = 0; off < PGLSZ(1); off += PGLSZ(0))
+ table[PTLX(va + off, 0)] = pa + off;
+ *pte = PADDR(table) | PTEVALID|PTEWRITE;
+ invlpg(va);
+}
+
+/*
+ * map kernel text segment readonly
+ * and everything else no-execute.
+ */
+static void
+kernelro(void)
+{+ uintptr *pte, psz, va;
+
+ ptesplit(m->pml4, APBOOTSTRAP);
+ ptesplit(m->pml4, KTZERO);
+ ptesplit(m->pml4, (uintptr)etext-1);
+
+ for(va = KZERO; va != 0; va += psz){+ psz = PGLSZ(0);
+ pte = mmuwalk(m->pml4, va, 0, 0);
+ if(pte == nil){+ if(va & PGLSZ(1)-1)
+ continue;
+ pte = mmuwalk(m->pml4, va, 1, 0);
+ if(pte == nil)
+ continue;
+ psz = PGLSZ(1);
+ }
+ if((*pte & PTEVALID) == 0)
+ continue;
+ if(va >= KTZERO && va < (uintptr)etext)
+ *pte &= ~PTEWRITE;
+ else if(va != (APBOOTSTRAP & -BY2PG))
+ *pte |= PTENOEXEC;
+ invlpg(va);
+ }
}
void
--- a/sys/src/9/pc64/squidboy.c
+++ b/sys/src/9/pc64/squidboy.c
@@ -79,6 +79,7 @@
apbootp[1] = (uintptr)PADDR(pml4);
apbootp[2] = (uintptr)apic;
apbootp[3] = (uintptr)mach;
+ apbootp[4] |= (uintptr)m->havenx<<11; /* EFER */
/*
* Universal Startup Algorithm.
--
⑨