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.
This commit is contained in:
cinap_lenrek 2019-08-27 03:47:18 +02:00
parent 128ea44a89
commit 2149600d12
13 changed files with 26 additions and 51 deletions

View file

@ -87,8 +87,6 @@ faultarm(Ureg *ureg, uintptr va, int user, int read)
{
int n, insyscall;
char buf[ERRMAX];
static int cnt, lastpid;
static ulong lastva;
if(up == nil) {
dumpregs(ureg);
@ -96,20 +94,7 @@ faultarm(Ureg *ureg, uintptr va, int user, int read)
}
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);

View file

@ -472,7 +472,7 @@ faultarm64(Ureg *ureg)
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 */

View file

@ -319,8 +319,6 @@ faultarm(Ureg *ureg, uintptr va, int user, int read)
{
int n, insyscall;
char buf[ERRMAX];
static int cnt, lastpid;
static ulong lastva;
if(up == nil) {
dumpregs(ureg);
@ -328,21 +326,7 @@ faultarm(Ureg *ureg, uintptr va, int user, int read)
}
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);

View file

@ -321,7 +321,7 @@ faultpower(Ureg *ureg, ulong addr, int read)
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);

View file

@ -333,7 +333,7 @@ faultarm(Ureg *ureg, uintptr va, int user, int read)
}
insyscall = up->insyscall;
up->insyscall = 1;
n = fault(va, read);
n = fault(va, ureg->pc, read);
if(n < 0){
if(!user){
dumpregs(ureg);

View file

@ -735,7 +735,7 @@ fault386(Ureg* ureg, void*)
insyscall = up->insyscall;
up->insyscall = 1;
n = fault(addr, read);
n = fault(addr, ureg->pc, read);
if(n < 0){
if(!user){
dumpregs(ureg);

View file

@ -708,7 +708,7 @@ faultamd64(Ureg* ureg, void*)
splx(s);
nexterror();
}
n = fault(addr, read);
n = fault(addr, ureg->pc, read);
if(n < 0){
if(!user){
dumpregs(ureg);

View file

@ -142,7 +142,6 @@ done:
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 @@ fixfault(Segment *s, uintptr addr, int read)
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 @@ fixfault(Segment *s, uintptr addr, int read)
(*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 @@ mapphys(Segment *s, uintptr addr, int attr)
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 @@ mapphys(Segment *s, uintptr addr, int attr)
}
int
fault(uintptr addr, int read)
fault(uintptr addr, uintptr pc, int read)
{
Segment *s;
char *sps;
@ -298,7 +302,9 @@ fault(uintptr addr, int read)
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 */

View file

@ -105,7 +105,7 @@ void exit(int);
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);

View file

@ -324,7 +324,7 @@ faultpower(Ureg *ureg, ulong addr, int read)
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);

View file

@ -607,7 +607,7 @@ faultarm(Ureg *ureg, uintptr va, int user, int read)
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];

View file

@ -598,7 +598,7 @@ unexpected(Ureg* ureg, void*)
}
static void
fault386(Ureg* ureg, void* )
fault386(Ureg* ureg, void*)
{
ulong addr;
int read, user, n, insyscall;
@ -621,7 +621,7 @@ fault386(Ureg* ureg, void* )
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);

View file

@ -98,7 +98,7 @@ faultarm(Ureg *ureg, ulong fsr, uintptr addr)
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: