diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2019-08-29 07:35:22 +0200 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2019-08-29 07:35:22 +0200 |
commit | 7bb1a9a18566ea9c8ae7f6c2fa99e448026521d2 (patch) | |
tree | af707e1d04c8211dd6ac2e191ebd659c442ef25b | |
parent | e988d56a2f6b87531a12559a336b5de4471605b4 (diff) |
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.
-rw-r--r-- | sys/src/9/pc64/apbootstrap.s | 5 | ||||
-rw-r--r-- | sys/src/9/pc64/main.c | 7 | ||||
-rw-r--r-- | sys/src/9/pc64/memory.c | 6 | ||||
-rw-r--r-- | sys/src/9/pc64/mmu.c | 60 | ||||
-rw-r--r-- | sys/src/9/pc64/squidboy.c | 1 |
5 files changed, 74 insertions, 5 deletions
diff --git a/sys/src/9/pc64/apbootstrap.s b/sys/src/9/pc64/apbootstrap.s index 951abd5f3..c93d54f09 100644 --- a/sys/src/9/pc64/apbootstrap.s +++ b/sys/src/9/pc64/apbootstrap.s @@ -42,6 +42,9 @@ TEXT _apapic(SB), 1, $-4 /* address APBOOTSTRAP+0x18 */ 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 @@ TEXT _ap32(SB), 1, $-4 MOVL $0xc0000080, CX /* Extended Feature Enable */ RDMSR - ORL $0x00000100, AX /* Long Mode Enable */ + ORL _apefer-KZERO(SB), AX WRMSR MOVL CR0, DX diff --git a/sys/src/9/pc64/main.c b/sys/src/9/pc64/main.c index c8e705f49..1abad73e4 100644 --- a/sys/src/9/pc64/main.c +++ b/sys/src/9/pc64/main.c @@ -301,6 +301,7 @@ static void rebootjump(uintptr entry, uintptr code, ulong size) { void (*f)(uintptr, uintptr, ulong); + uintptr *pte; splhi(); arch->introff(); @@ -310,6 +311,12 @@ rebootjump(uintptr entry, uintptr code, ulong size) */ *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 */ diff --git a/sys/src/9/pc64/memory.c b/sys/src/9/pc64/memory.c index d737e6494..c2360abb3 100644 --- a/sys/src/9/pc64/memory.c +++ b/sys/src/9/pc64/memory.c @@ -221,6 +221,8 @@ rampage(void) { 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 @@ map(uintptr base, uintptr len, int type) 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); diff --git a/sys/src/9/pc64/mmu.c b/sys/src/9/pc64/mmu.c index 6db61bf00..8560eb03c 100644 --- a/sys/src/9/pc64/mmu.c +++ b/sys/src/9/pc64/mmu.c @@ -73,6 +73,8 @@ taskswitch(uintptr stack) mmuflushtlb(); } +static void kernelro(void); + void mmuinit(void) { @@ -84,6 +86,9 @@ mmuinit(void) 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 @@ mmucreate(uintptr *table, uintptr va, int level, int index) up->kmapcount++; } page = p->page; - } else if(conf.mem[0].npage != 0) { - page = mallocalign(PTSZ, BY2PG, 0, 0); } else { page = rampage(); } @@ -287,6 +290,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 pmap(uintptr *pml4, uintptr pa, uintptr va, vlong size) { diff --git a/sys/src/9/pc64/squidboy.c b/sys/src/9/pc64/squidboy.c index e920c413b..dda4f257f 100644 --- a/sys/src/9/pc64/squidboy.c +++ b/sys/src/9/pc64/squidboy.c @@ -79,6 +79,7 @@ mpstartap(Apic* apic) apbootp[1] = (uintptr)PADDR(pml4); apbootp[2] = (uintptr)apic; apbootp[3] = (uintptr)mach; + apbootp[4] |= (uintptr)m->havenx<<11; /* EFER */ /* * Universal Startup Algorithm. |