POSIX specifies these error cases for memory faults:
SIGSEGV/SEGV_MAPERR: Accessing an unmapped page.
SIGSEGV/SEGV_ACCERR: Reading from a non-readable or writing to a
non-writable page.
SIGBUS/BUS_ADRERR: Accessing a mapped page that exceeds the end of
the underlying mapped file.
I added a regress test at regress/sys/kern/siginfo-fault to cover
these cases, but unfortunately we're non-compliant in a few ways, and
fixing it is somewhat MD. With the diff below, the tests pass on
amd64, but other platforms will need similar changes.
Currently VM_PAGER_BAD is only returned by pgo_get() in the case of
uvn_get() trying to access a page beyond the end of the file, so this
diff changes uvm_fault() to recognize this and return ENOSPC
(arbitrary unused error code) and then the MD trap() code needs to
know to map this error to BUS_ADRERR.
Additionally, some MD trap()s already know to map EACCES to
SEGV_ACCERR instead of SEGV_MAPERR, but amd64 wasn't one of them. So
this diff fixes that too.
Index: uvm/uvm_fault.c
===================================================================
RCS file: /home/matthew/cvs-mirror/cvs/src/sys/uvm/uvm_fault.c,v
retrieving revision 1.73
diff -u -p -r1.73 uvm_fault.c
--- uvm/uvm_fault.c 8 May 2014 20:08:50 -0000 1.73
+++ uvm/uvm_fault.c 23 Jun 2014 21:29:24 -0000
@@ -1125,7 +1125,8 @@ Case2:
goto ReFault;
}
- return (EACCES); /* XXX i/o error */
+ /* XXX i/o error */
+ return (result == VM_PAGER_BAD ? ENOSPC : EACCES);
}
/* re-verify the state of the world. */
Index: arch/amd64/amd64/trap.c
===================================================================
RCS file: /home/matthew/cvs-mirror/cvs/src/sys/arch/amd64/amd64/trap.c,v
retrieving revision 1.40
diff -u -p -r1.40 trap.c
--- arch/amd64/amd64/trap.c 15 Jun 2014 11:43:24 -0000 1.40
+++ arch/amd64/amd64/trap.c 23 Jun 2014 21:38:31 -0000
@@ -387,9 +387,6 @@ faultcommon:
KERNEL_UNLOCK();
goto out;
}
- if (error == EACCES) {
- error = EFAULT;
- }
if (type == T_PAGEFLT) {
if (pcb->pcb_onfault != 0) {
@@ -407,13 +404,23 @@ faultcommon:
sv.sival_ptr = (void *)fa;
trapsignal(p, SIGKILL, T_PAGEFLT, SEGV_MAPERR, sv);
} else {
+ int signo, code;
+ if (error == ENOSPC) {
+ signo = SIGBUS;
+ code = BUS_ADRERR;
+ } else {
+ signo = SIGSEGV;
+ code = (error == EACCES) ? SEGV_ACCERR :
+ SEGV_MAPERR;
+ }
#ifdef TRAP_SIGDEBUG
- printf("pid %d (%s): SEGV at rip %lx addr %lx\n",
- p->p_pid, p->p_comm, frame->tf_rip, fa);
+ printf("pid %d (%s): %s at rip %lx addr %lx\n",
+ p->p_pid, p->p_comm, (signo == SIGBUS) ?
+ "BUS" : "SEGV", frame->tf_rip, fa);
frame_dump(frame);
#endif
sv.sival_ptr = (void *)fa;
- trapsignal(p, SIGSEGV, T_PAGEFLT, SEGV_MAPERR, sv);
+ trapsignal(p, signo, T_PAGEFLT, code, sv);
}
KERNEL_UNLOCK();
break;