Browse Source

add no-exec mmu support, set the no-exec bit in k10/mmu.c too
updated for the latest changes, also make regress/nx more comprehensive.
regress/nx PASS

Change-Id: Ib892ab499774b669c0fded691931624519fa51da

Aki Nyrhinen 8 years ago
parent
commit
293d699551

+ 5 - 3
sys/src/9/k10/mmu.c

@@ -353,13 +353,13 @@ mmuptpcheck(Proc *proc)
 	}
 }
 
-static uint
+static uintmem
 pteflags(uint attr)
 {
-	uint flags;
+	uintmem flags;
 
 	flags = 0;
-	if(attr & ~(PTEVALID|PTEWRITE|PTERONLY|PTEUSER|PTEUNCACHED))
+	if(attr & ~(PTEVALID|PTEWRITE|PTERONLY|PTEUSER|PTEUNCACHED|PTENOEXEC))
 		panic("mmuput: wrong attr bits: %#ux\n", attr);
 	if(attr&PTEVALID)
 		flags |= PteP;
@@ -369,6 +369,8 @@ pteflags(uint attr)
 		flags |= PteU;
 	if(attr&PTEUNCACHED)
 		flags |= PtePCD;
+	if(attr&PTENOEXEC)
+		flags |= PteNX;
 	return flags;
 }
 

+ 6 - 5
sys/src/9/k10/trap.c

@@ -664,7 +664,7 @@ faultamd64(Ureg* ureg, void* v)
 {
 	Proc *up = machp()->externup;
 	uint64_t addr;
-	int read, user, insyscall;
+	int ftype, user, insyscall;
 	char buf[ERRMAX];
 
 	addr = machp()->cr2;
@@ -681,7 +681,8 @@ faultamd64(Ureg* ureg, void* v)
 		panic("fault with up == nil; pc %#llux addr %#llux\n",
 			ureg->ip, addr);
 	}
-	read = !(ureg->error & 2);
+
+	ftype = (ureg->error&2) ? FT_WRITE : (ureg->error&16) ? FT_EXEC : FT_READ;
 /*
 if (read) hi("read fault\n"); else hi("write fault\n");
 hi("addr "); put64(addr); hi("\n");
@@ -691,8 +692,8 @@ hi("addr "); put64(addr); hi("\n");
 	up->insyscall = 1;
 	if (0)hi("call fault\n");
 
-	if(fault(addr, read) < 0){
-iprint("could not fault %p\n", addr);
+	if(fault(addr, ureg->ip, ftype) < 0){
+iprint("could not %s fault %p\n", faulttypes[ftype], addr);
 	if (! user)
 		panic("fault went bad in kernel\n");
 	else
@@ -711,7 +712,7 @@ iprint("could not fault %p\n", addr);
 		if(!user && (!insyscall || up->nerrlab == 0))
 			panic("fault: %#llux\n", addr);
 		sprint(buf, "sys: trap: fault %s addr=%#llux",
-			read? "read": "write", addr);
+			faulttypes[ftype], addr);
 		postnote(up, 1, buf, NDebug);
 		if(insyscall)
 			error(buf);

+ 52 - 25
sys/src/9/port/fault.c

@@ -17,6 +17,12 @@
 #undef DBG
 #define DBG if(0)print
 
+char *faulttypes[] = {
+	[FT_WRITE] "write",
+	[FT_READ] "read",
+	[FT_EXEC] "exec"
+};
+
 
 /*
  * Fault calls fixfault which ends up calling newpage, which
@@ -26,7 +32,7 @@
  * other one, if we failed for some time.
  */
 int
-fault(uintptr_t addr, int read)
+fault(uintptr_t addr, uintptr_t pc, int ftype)
 {
 	Proc *up = machp()->externup;
 	Segment *s;
@@ -34,10 +40,10 @@ fault(uintptr_t addr, int read)
 	int i, color;
 
 	if(up->nlocks)
-		print("%s fault nlocks %d addr %p\n",
-			read ? "read" : "write",
+		print("%s fault nlocks %d addr %p pc %p\n",
+			faulttypes[ftype],
 			up->nlocks,
-			addr);
+			addr, pc);
 
 	sps = up->psstate;
 	up->psstate = "Fault";
@@ -46,23 +52,20 @@ fault(uintptr_t addr, int read)
 	machp()->pfault++;
 	for(i = 0;; i++) {
 		s = seg(up, addr, 1);	 /* leaves s->lk qlocked if seg != nil */
-		//iprint("seg for %p is %p base %p top %p\n", addr, s, s->base, s->top);
-		if(s == 0) {
-			//iprint("fault: no seg for %p\n", addr);
-			up->psstate = sps;
-			return -1;
-		}
-
-		if(!read && (s->type&SG_WRITE) == 0) {
-			qunlock(&s->lk);
-			up->psstate = sps;
-			return -1;
-		}
+		//print("%s fault seg for %p is %p base %p top %p\n", faulttypes[ftype], addr, s, s->base, s->top);
+		if(s == nil)
+			goto fail;
+		if(ftype == FT_READ && (s->type&SG_READ) == 0)
+			goto fail;
+		if(ftype == FT_WRITE && (s->type&SG_WRITE) == 0)
+			goto fail;
+		if(ftype == FT_EXEC && (s->type&SG_EXEC) == 0)
+			goto fail;
 
 		color = s->color;
 		if(i > 3)
 			color = -1;
-		if(fixfault(s, addr, read, 1, color) == 0)
+		if(fixfault(s, addr, ftype, 1, color) == 0)
 			break;
 
 		/*
@@ -76,6 +79,23 @@ fault(uintptr_t addr, int read)
 
 	up->psstate = sps;
 	return 0;
+fail:
+	if(s != nil){
+		qunlock(&s->lk);
+		print("%s fault fail %s(%c%c%c) pid %d addr 0x%p pc 0x%p\n",
+			faulttypes[ftype],
+			segtypes[s->type & SG_TYPE],
+			(s->type & SG_READ) != 0 ? 'r' : '-',
+			(s->type & SG_WRITE) != 0 ? 'w' : '-',
+			(s->type & SG_EXEC) != 0 ? 'x' : '-',
+			up->pid, addr, pc);
+	} else {
+		print("%s fault fail, no segment, pid %d addr 0x%p pc 0x%p\n",
+			faulttypes[ftype],
+			up->pid, addr, pc);
+	}
+	up->psstate = sps;
+	return -1;
 }
 
 static void
@@ -98,10 +118,10 @@ faulterror(char *s, Chan *c, int freemem)
 }
 
 int
-fixfault(Segment *s, uintptr_t addr, int read, int dommuput, int color)
+fixfault(Segment *s, uintptr_t addr, int ftype, int dommuput, int color)
 {
 	Proc *up = machp()->externup;
-	int type;
+	int stype;
 	int ref;
 	Pte **p, *etp;
 	uintptr_t soff;
@@ -119,7 +139,7 @@ fixfault(Segment *s, uintptr_t addr, int read, int dommuput, int color)
 
 	etp = *p;
 	pg = &etp->pages[(soff&(PTEMAPMEM-1))/pgsz];
-	type = s->type&SG_TYPE;
+	stype = s->type&SG_TYPE;
 
 	if(pg < etp->first)
 		etp->first = pg;
@@ -127,7 +147,7 @@ fixfault(Segment *s, uintptr_t addr, int read, int dommuput, int color)
 		etp->last = pg;
 
 	mmuattr = 0;
-	switch(type) {
+	switch(stype) {
 	default:
 		panic("fault");
 		break;
@@ -168,18 +188,21 @@ fixfault(Segment *s, uintptr_t addr, int read, int dommuput, int color)
 
 	common:			/* Demand load/pagein/copy on write */
 
-		if(read){
+		if(ftype != FT_WRITE){
 			/* never copy a non-writeable seg */
 			if((s->type & SG_WRITE) == 0){
 				mmuattr = PTERONLY|PTEVALID;
+				if((s->type & SG_EXEC) == 0)
+					mmuattr |= PTENOEXEC;
 				(*pg)->modref = PG_REF;
 				break;
 			}
 
 			/* delay copy if we are the only user (copy on write when it happens) */
 			if(conf.copymode == 0 && s->ref == 1) {
-
 				mmuattr = PTERONLY|PTEVALID;
+				if((s->type & SG_EXEC) == 0)
+					mmuattr |= PTENOEXEC;
 				(*pg)->modref |= PG_REF;
 				break;
 			}
@@ -201,8 +224,8 @@ fixfault(Segment *s, uintptr_t addr, int read, int dommuput, int color)
 
 				DBG("fixfault %d: copy on %s, %s(%c%c%c) 0x%p segref %d pgref %d\n",
 					up->pid,
-					read ? "read " : "write",
-					segtypes[s->type & SG_TYPE],
+					faulttypes[ftype],
+					segtypes[stype],
 					(s->type & SG_READ) != 0 ? 'r' : '-',
 					(s->type & SG_WRITE) != 0 ? 'w' : '-',
 					(s->type & SG_EXEC) != 0 ? 'x' : '-',
@@ -226,6 +249,8 @@ fixfault(Segment *s, uintptr_t addr, int read, int dommuput, int color)
 			}
 		}
 		mmuattr = PTEVALID|PTEWRITE;
+		if((s->type & SG_EXEC) == 0)
+			mmuattr |= PTENOEXEC;
 		(*pg)->modref = PG_MOD|PG_REF;
 		break;
 
@@ -249,6 +274,8 @@ fixfault(Segment *s, uintptr_t addr, int read, int dommuput, int color)
 			mmuattr |= PTEWRITE;
 		if((s->pseg->attr & SG_CACHED) == 0)
 			mmuattr |= PTEUNCACHED;
+		if((s->type & SG_EXEC) == 0)
+			mmuattr |= PTENOEXEC;
 		(*pg)->modref = PG_MOD|PG_REF;
 		break;
 	}

+ 13 - 4
sys/src/9/port/portdat.h

@@ -416,10 +416,11 @@ struct Image
 /*
  * Interface between fixfault and mmuput.
  */
-#define PTEVALID		(1<<0)
-#define PTEWRITE		(1<<1)
-#define PTERONLY		(0<<1)
-#define PTEUSER		(1<<2)
+#define PTEVALID	(1<<0)
+#define PTEWRITE	(1<<1)
+#define PTERONLY	(0<<1)
+#define PTEUSER	(1<<2)
+#define PTENOEXEC	(1<<3)
 #define PTEUNCACHED	(1<<4)
 
 struct Pte
@@ -457,6 +458,14 @@ enum
 };
 extern char *segtypes[]; /* port/segment.c */
 
+enum
+{
+	FT_WRITE = 0,
+	FT_READ,
+	FT_EXEC,
+};
+extern char *faulttypes[]; /* port/fault.c */
+
 #define PG_ONSWAP	1
 #define onswap(s)	(PTR2UINT(s) & PG_ONSWAP)
 #define pagedout(s)	(PTR2UINT(s) == 0 || onswap(s))

+ 1 - 1
sys/src/9/port/portfns.h

@@ -122,7 +122,7 @@ void		exit(int);
 uint64_t		fastticks(uint64_t*);
 uint64_t		fastticks2ns(uint64_t);
 uint64_t		fastticks2us(uint64_t);
-int		fault(uintptr_t, int);
+int		fault(uintptr_t, uintptr_t, int);
 void		fdclose(int, int);
 Chan*		fdtochan(int, int, int, int);
 int		findmount(Chan**, Mhead**, int, uint, Qid);

+ 1 - 1
sys/src/9/port/segment.c

@@ -405,7 +405,7 @@ prepageseg(int i)
 	DBG("prepage: base %#p top %#p\n", s->base, s->top);
 	pgsz = machp()->pgsz[s->pgszi];
 	for(addr = s->base; addr < s->top; addr += pgsz)
-		fault(addr, (s->type & SG_EXEC) != 0);
+		fault(addr, -1, (s->type & SG_WRITE) ? FT_WRITE : FT_READ);
 }
 
 /*

+ 80 - 7
sys/src/regress/nx.c

@@ -2,15 +2,18 @@
 #include <libc.h>
 #define RET 0xc3
 
+int success;
+int cases;
+
 void
 handler(void *v, char *s)
 {
-	print("PASS\n");
-	exits("PASS");
+	success++;
+	exits(nil);
 }
 
 void
-main(void)
+callinsn(char *name, char *buf)
 {
 	void (*f)(void);
 	if (notify(handler)){
@@ -18,10 +21,80 @@ main(void)
 		exits("notify fails");
 	}
 
-	uint8_t *ret = malloc(1);
-	*ret = RET;
-	f = (void *)ret;
+
+	f = (void *)buf;
 	f();
-	print("FAIL");
+	print("FAIL %s\n", name);
+	exits("FAIL");
+}
+
+void
+writeptr(char *name, void *ptr)
+{
+	if (notify(handler)){
+		fprint(2, "%r\n");
+		exits("notify fails");
+	}
+
+	*(uintptr_t*)ptr = 0xdeadbeef;
+	print("FAIL %s\n", name);
+	exits("FAIL");
+}
+
+void
+main(void)
+{
+	char *str = "hello world";
+	char stk[128];
+	char *mem;
+
+	switch(rfork(RFMEM|RFPROC)){
+	case -1:
+		sysfatal("rfork");
+	case 0:
+		stk[0] = RET;
+		callinsn("exec stack", stk);
+	default:
+		cases++;
+		waitpid();
+	}
+
+	switch(rfork(RFMEM|RFPROC)){
+	case -1:
+		sysfatal("rfork");
+	case 0:
+		mem = malloc(128);
+		mem[0] = RET;
+		callinsn("exec heap", mem);
+	default:
+		cases++;
+		waitpid();
+	}
+
+	switch(rfork(RFMEM|RFPROC)){
+	case -1:
+		sysfatal("rfork");
+	case 0:
+		writeptr("write code", (void*)&main);
+	default:
+		cases++;
+		waitpid();
+	}
+
+	switch(rfork(RFMEM|RFPROC)){
+	case -1:
+		sysfatal("rfork");
+	case 0:
+		writeptr("write rodata", (void*)str);
+	default:
+		cases++;
+		waitpid();
+	}
+
+	if(success == cases){
+		print("PASS\n");
+		exits("PASS");
+	}
+	print("FAIL\n");
 	exits("FAIL");
 }