shithub: riscv

Download patch

ref: 2149600d129944f60cbc858bc669193af0523409
parent: 128ea44a89c7905612ad2fa5a61a9325ddfb5e1e
author: cinap_lenrek <[email protected]>
date: Mon Aug 26 23:47:18 EDT 2019

kernel: catch execution read fault on SG_NOEXEC segment

fault() now has an additional pc argument that is
used to detect fault on a non-executable segment.
that is, we check on read fault if the segment
has the SG_NOEXEC attribute and the program counter
is within faulting page.

--- a/sys/src/9/bcm/trap.c
+++ b/sys/src/9/bcm/trap.c
@@ -87,8 +87,6 @@
 {
 	int n, insyscall;
 	char buf[ERRMAX];
-	static int cnt, lastpid;
-	static ulong lastva;
 
 	if(up == nil) {
 		dumpregs(ureg);
@@ -96,20 +94,7 @@
 	}
 	insyscall = up->insyscall;
 	up->insyscall = 1;
-	/* this is quite helpful during mmu and cache debugging */
-	if(va == lastva && up->pid == lastpid) {
-		++cnt;
-		if (cnt >= 2)
-			/* fault() isn't fixing the underlying cause */
-			panic("fault: %d consecutive faults for va %#lux",
-				cnt+1, va);
-	} else {
-		cnt = 0;
-		lastva = va;
-		lastpid = up->pid;
-	}
-
-	n = fault(va, read);
+	n = fault(va, ureg->pc, read);
 	if(n < 0){
 		if(!user){
 			dumpregs(ureg);
--- a/sys/src/9/bcm64/trap.c
+++ b/sys/src/9/bcm64/trap.c
@@ -472,7 +472,7 @@
 	case  8: case  9: case 10: case 11:	// Access flag fault.
 	case 12: case 13: case 14: case 15:	// Permission fault.
 	case 48:				// tlb conflict fault.
-		if(fault(addr, read) == 0)
+		if(fault(addr, ureg->pc, read) == 0)
 			break;
 
 		/* wet floor */
--- a/sys/src/9/kw/trap.c
+++ b/sys/src/9/kw/trap.c
@@ -319,8 +319,6 @@
 {
 	int n, insyscall;
 	char buf[ERRMAX];
-	static int cnt, lastpid;
-	static ulong lastva;
 
 	if(up == nil) {
 		dumpregs(ureg);
@@ -328,21 +326,7 @@
 	}
 	insyscall = up->insyscall;
 	up->insyscall = 1;
-
-	/* this is quite helpful during mmu and cache debugging */
-	if(va == lastva && up->pid == lastpid) {
-		++cnt;
-		if (cnt >= 2)
-			/* fault() isn't fixing the underlying cause */
-			panic("fault: %d consecutive faults for va %#lux",
-				cnt+1, va);
-	} else {
-		cnt = 0;
-		lastva = va;
-		lastpid = up->pid;
-	}
-
-	n = fault(va, read);
+	n = fault(va, ureg->pc, read);
 	if(n < 0){
 		if(!user){
 			dumpregs(ureg);
--- a/sys/src/9/mtx/trap.c
+++ b/sys/src/9/mtx/trap.c
@@ -321,7 +321,7 @@
 	user = (ureg->srr1 & MSR_PR) != 0;
 	insyscall = up->insyscall;
 	up->insyscall = 1;
-	n = fault(addr, read);
+	n = fault(addr, ureg->pc, read);
 	if(n < 0){
 		if(!user){
 			dumpregs(ureg);
--- a/sys/src/9/omap/trap.c
+++ b/sys/src/9/omap/trap.c
@@ -333,7 +333,7 @@
 	}
 	insyscall = up->insyscall;
 	up->insyscall = 1;
-	n = fault(va, read);
+	n = fault(va, ureg->pc, read);
 	if(n < 0){
 		if(!user){
 			dumpregs(ureg);
--- a/sys/src/9/pc/trap.c
+++ b/sys/src/9/pc/trap.c
@@ -735,7 +735,7 @@
 
 	insyscall = up->insyscall;
 	up->insyscall = 1;
-	n = fault(addr, read);
+	n = fault(addr, ureg->pc, read);
 	if(n < 0){
 		if(!user){
 			dumpregs(ureg);
--- a/sys/src/9/pc64/trap.c
+++ b/sys/src/9/pc64/trap.c
@@ -708,7 +708,7 @@
 		splx(s);
 		nexterror();
 	}
-	n = fault(addr, read);
+	n = fault(addr, ureg->pc, read);
 	if(n < 0){
 		if(!user){
 			dumpregs(ureg);
--- a/sys/src/9/port/fault.c
+++ b/sys/src/9/port/fault.c
@@ -142,7 +142,6 @@
 static int
 fixfault(Segment *s, uintptr addr, int read)
 {
-	int type;
 	Pte **pte, *etp;
 	uintptr soff, mmuphys;
 	Page **pg, *old, *new;
@@ -159,8 +158,7 @@
 	if(pg > etp->last)
 		etp->last = pg;
 
-	type = s->type & SG_TYPE;
-	switch(type) {
+	switch(s->type & SG_TYPE) {
 	default:
 		panic("fault");
 		return -1;
@@ -221,6 +219,12 @@
 		(*pg)->modref = PG_MOD|PG_REF;
 		break;
 	}
+
+#ifdef PTENOEXEC
+	if((s->type & SG_NOEXEC) != 0)
+		mmuphys |= PTENOEXEC;
+#endif
+
 	qunlock(s);
 
 	putmmu(addr, mmuphys, *pg);
@@ -246,12 +250,12 @@
 		mmuphys |= PTERONLY;
 
 #ifdef PTENOEXEC
-	if((attr & SG_NOEXEC) == SG_NOEXEC)
+	if((attr & SG_NOEXEC) != 0)
 		mmuphys |= PTENOEXEC;
 #endif
 
 #ifdef PTEDEVICE
-	if((attr & SG_DEVICE) == SG_DEVICE)
+	if((attr & SG_DEVICE) != 0)
 		mmuphys |= PTEDEVICE;
 	else
 #endif
@@ -266,7 +270,7 @@
 }
 
 int
-fault(uintptr addr, int read)
+fault(uintptr addr, uintptr pc, int read)
 {
 	Segment *s;
 	char *sps;
@@ -298,7 +302,9 @@
 		if((attr & SG_TYPE) == SG_PHYSICAL)
 			attr |= s->pseg->attr;
 
-		if((attr & SG_FAULT) != 0 || !read && (attr & SG_RONLY) != 0) {
+		if((attr & SG_FAULT) != 0
+		|| read? (attr & SG_NOEXEC) != 0 && (addr & -BY2PG) == (pc & -BY2PG):
+			 (attr & SG_RONLY) != 0) {
 			qunlock(s);
 			up->psstate = sps;
 			if(up->kp && up->nerrlab)	/* for segio */
--- a/sys/src/9/port/portfns.h
+++ b/sys/src/9/port/portfns.h
@@ -105,7 +105,7 @@
 uvlong		fastticks(uvlong*);
 uvlong		fastticks2ns(uvlong);
 uvlong		fastticks2us(uvlong);
-int		fault(uintptr, int);
+int		fault(uintptr, uintptr, int);
 void		fdclose(int, int);
 Chan*		fdtochan(int, int, int, int);
 int		findmount(Chan**, Mhead**, int, int, Qid);
--- a/sys/src/9/ppc/trap.c
+++ b/sys/src/9/ppc/trap.c
@@ -324,7 +324,7 @@
 	user = (ureg->srr1 & MSR_PR) != 0;
 	insyscall = up->insyscall;
 	up->insyscall = 1;
-	n = fault(addr, read);
+	n = fault(addr, ureg->pc, read);
 	if(n < 0){
 		if(!user){
 			dumpregs(ureg);
--- a/sys/src/9/teg2/trap.c
+++ b/sys/src/9/teg2/trap.c
@@ -607,7 +607,7 @@
 	insyscall = up->insyscall;
 	up->insyscall = 1;
 
-	n = fault(va, read);		/* goes spllo */
+	n = fault(va, ureg->pc, read);		/* goes spllo */
 	splhi();
 	if(n < 0){
 		char buf[ERRMAX];
--- a/sys/src/9/xen/trap.c
+++ b/sys/src/9/xen/trap.c
@@ -598,7 +598,7 @@
 }
 
 static void
-fault386(Ureg* ureg, void* )
+fault386(Ureg* ureg, void*)
 {
 	ulong addr;
 	int read, user, n, insyscall;
@@ -621,7 +621,7 @@
 		panic("fault but up is zero; pc 0x%8.8lux addr 0x%8.8lux\n", ureg->pc, addr);
 	insyscall = up->insyscall;
 	up->insyscall = 1;
-	n = fault(addr, read);
+	n = fault(addr, ureg->pc, read);
 	if(n < 0){
 		if(!user){
 			dumpregs(ureg);
--- a/sys/src/9/zynq/trap.c
+++ b/sys/src/9/zynq/trap.c
@@ -98,7 +98,7 @@
 	case 0x0B:	/* domain fault L2 */
 	case 0x0D:	/* permission fault L1 */
 	case 0x0F:	/* permission fault L2 */
-		if(fault(addr, read) == 0)
+		if(fault(addr, ureg->pc, read) == 0)
 			break;
 		/* wet floor */
 	default: