Re: SIGBUS short mmap object
> Date: Thu, 20 Jul 2017 18:18:58 +0200 > From: Alexander Bluhm> > On Thu, Jul 20, 2017 at 02:03:10PM +0200, Mark Kettenis wrote: > > If you can make the BUS_ADRERR -> BUS_OBJERR change (both here and in > > the regress test), this is ok kettenis@ > > This passes on amd64 and i386. > > ok? ok kettenis@ The man page will need some further tweaking once other architectures catch up. > Index: sys/arch/amd64/amd64/trap.c > === > RCS file: /data/mirror/openbsd/cvs/src/sys/arch/amd64/amd64/trap.c,v > retrieving revision 1.55 > diff -u -p -r1.55 trap.c > --- sys/arch/amd64/amd64/trap.c 14 Jul 2017 12:20:32 - 1.55 > +++ sys/arch/amd64/amd64/trap.c 20 Jul 2017 15:59:37 - > @@ -306,6 +306,7 @@ copyfault: > struct vm_map *map; > vm_prot_t ftype; > extern struct vm_map *kernel_map; > + int signal, sicode; > > cr2 = rcr2(); > KERNEL_LOCK(); > @@ -372,12 +373,14 @@ faultcommon: > map, fa, ftype, error); > goto we_re_toast; > } > + > + signal = SIGSEGV; > + sicode = SEGV_MAPERR; > if (error == ENOMEM) { > printf("UVM: pid %d (%s), uid %d killed:" > " out of swap\n", p->p_p->ps_pid, p->p_p->ps_comm, > p->p_ucred ? (int)p->p_ucred->cr_uid : -1); > - sv.sival_ptr = (void *)fa; > - trapsignal(p, SIGKILL, T_PAGEFLT, SEGV_MAPERR, sv); > + signal = SIGKILL; > } else { > #ifdef TRAP_SIGDEBUG > printf("pid %d (%s): %s at rip %llx addr %llx\n", > @@ -385,10 +388,15 @@ faultcommon: > frame->tf_rip, rcr2()); > frame_dump(frame); > #endif > - sv.sival_ptr = (void *)fa; > - trapsignal(p, SIGSEGV, T_PAGEFLT, > - error == EACCES ? SEGV_ACCERR : SEGV_MAPERR, sv); > } > + if (error == EACCES) > + sicode = SEGV_ACCERR; > + if (error == EIO) { > + signal = SIGBUS; > + sicode = BUS_OBJERR; > + } > + sv.sival_ptr = (void *)fa; > + trapsignal(p, signal, T_PAGEFLT, sicode, sv); > KERNEL_UNLOCK(); > break; > } > Index: sys/arch/i386/i386/trap.c > === > RCS file: /data/mirror/openbsd/cvs/src/sys/arch/i386/i386/trap.c,v > retrieving revision 1.131 > diff -u -p -r1.131 trap.c > --- sys/arch/i386/i386/trap.c 14 Jul 2017 12:20:32 - 1.131 > +++ sys/arch/i386/i386/trap.c 20 Jul 2017 16:00:16 - > @@ -374,6 +374,7 @@ trap(struct trapframe *frame) > struct vmspace *vm; > struct vm_map *map; > int error; > + int signal, sicode; > > cr2 = rcr2(); > KERNEL_LOCK(); > @@ -431,9 +432,17 @@ trap(struct trapframe *frame) > map, va, ftype, error); > goto we_re_toast; > } > + > + signal = SIGSEGV; > + sicode = SEGV_MAPERR; > + if (error == EACCES) > + sicode = SEGV_ACCERR; > + if (error == EIO) { > + signal = SIGBUS; > + sicode = BUS_OBJERR; > + } > sv.sival_int = fa; > - trapsignal(p, SIGSEGV, vftype, > - error == EACCES ? SEGV_ACCERR : SEGV_MAPERR, sv); > + trapsignal(p, signal, vftype, sicode, sv); > KERNEL_UNLOCK(); > break; > } > Index: sys/uvm/uvm_fault.c > === > RCS file: /data/mirror/openbsd/cvs/src/sys/uvm/uvm_fault.c,v > retrieving revision 1.91 > diff -u -p -r1.91 uvm_fault.c > --- sys/uvm/uvm_fault.c 16 Sep 2016 01:09:53 - 1.91 > +++ sys/uvm/uvm_fault.c 20 Jul 2017 15:56:46 - > @@ -1032,7 +1032,7 @@ Case2: > } > > if (!UVM_ET_ISNOFAULT(ufi.entry)) > - return (EACCES); /* XXX i/o error */ > + return (EIO); > > uobjpage = PGO_DONTCARE; > promote = TRUE; > Index: regress/sys/kern/siginfo-fault/siginfo-fault.c > === > RCS file: > /data/mirror/openbsd/cvs/src/regress/sys/kern/siginfo-fault/siginfo-fault.c,v > retrieving revision 1.4 > diff -u -p -r1.4 siginfo-fault.c > --- regress/sys/kern/siginfo-fault/siginfo-fault.c13 Jul 2017 00:29:14 > - 1.4 > +++
Re: SIGBUS short mmap object
On Thu, Jul 20, 2017 at 02:03:10PM +0200, Mark Kettenis wrote: > If you can make the BUS_ADRERR -> BUS_OBJERR change (both here and in > the regress test), this is ok kettenis@ This passes on amd64 and i386. ok? bluhm Index: sys/arch/amd64/amd64/trap.c === RCS file: /data/mirror/openbsd/cvs/src/sys/arch/amd64/amd64/trap.c,v retrieving revision 1.55 diff -u -p -r1.55 trap.c --- sys/arch/amd64/amd64/trap.c 14 Jul 2017 12:20:32 - 1.55 +++ sys/arch/amd64/amd64/trap.c 20 Jul 2017 15:59:37 - @@ -306,6 +306,7 @@ copyfault: struct vm_map *map; vm_prot_t ftype; extern struct vm_map *kernel_map; + int signal, sicode; cr2 = rcr2(); KERNEL_LOCK(); @@ -372,12 +373,14 @@ faultcommon: map, fa, ftype, error); goto we_re_toast; } + + signal = SIGSEGV; + sicode = SEGV_MAPERR; if (error == ENOMEM) { printf("UVM: pid %d (%s), uid %d killed:" " out of swap\n", p->p_p->ps_pid, p->p_p->ps_comm, p->p_ucred ? (int)p->p_ucred->cr_uid : -1); - sv.sival_ptr = (void *)fa; - trapsignal(p, SIGKILL, T_PAGEFLT, SEGV_MAPERR, sv); + signal = SIGKILL; } else { #ifdef TRAP_SIGDEBUG printf("pid %d (%s): %s at rip %llx addr %llx\n", @@ -385,10 +388,15 @@ faultcommon: frame->tf_rip, rcr2()); frame_dump(frame); #endif - sv.sival_ptr = (void *)fa; - trapsignal(p, SIGSEGV, T_PAGEFLT, - error == EACCES ? SEGV_ACCERR : SEGV_MAPERR, sv); } + if (error == EACCES) + sicode = SEGV_ACCERR; + if (error == EIO) { + signal = SIGBUS; + sicode = BUS_OBJERR; + } + sv.sival_ptr = (void *)fa; + trapsignal(p, signal, T_PAGEFLT, sicode, sv); KERNEL_UNLOCK(); break; } Index: sys/arch/i386/i386/trap.c === RCS file: /data/mirror/openbsd/cvs/src/sys/arch/i386/i386/trap.c,v retrieving revision 1.131 diff -u -p -r1.131 trap.c --- sys/arch/i386/i386/trap.c 14 Jul 2017 12:20:32 - 1.131 +++ sys/arch/i386/i386/trap.c 20 Jul 2017 16:00:16 - @@ -374,6 +374,7 @@ trap(struct trapframe *frame) struct vmspace *vm; struct vm_map *map; int error; + int signal, sicode; cr2 = rcr2(); KERNEL_LOCK(); @@ -431,9 +432,17 @@ trap(struct trapframe *frame) map, va, ftype, error); goto we_re_toast; } + + signal = SIGSEGV; + sicode = SEGV_MAPERR; + if (error == EACCES) + sicode = SEGV_ACCERR; + if (error == EIO) { + signal = SIGBUS; + sicode = BUS_OBJERR; + } sv.sival_int = fa; - trapsignal(p, SIGSEGV, vftype, - error == EACCES ? SEGV_ACCERR : SEGV_MAPERR, sv); + trapsignal(p, signal, vftype, sicode, sv); KERNEL_UNLOCK(); break; } Index: sys/uvm/uvm_fault.c === RCS file: /data/mirror/openbsd/cvs/src/sys/uvm/uvm_fault.c,v retrieving revision 1.91 diff -u -p -r1.91 uvm_fault.c --- sys/uvm/uvm_fault.c 16 Sep 2016 01:09:53 - 1.91 +++ sys/uvm/uvm_fault.c 20 Jul 2017 15:56:46 - @@ -1032,7 +1032,7 @@ Case2: } if (!UVM_ET_ISNOFAULT(ufi.entry)) - return (EACCES); /* XXX i/o error */ + return (EIO); uobjpage = PGO_DONTCARE; promote = TRUE; Index: regress/sys/kern/siginfo-fault/siginfo-fault.c === RCS file: /data/mirror/openbsd/cvs/src/regress/sys/kern/siginfo-fault/siginfo-fault.c,v retrieving revision 1.4 diff -u -p -r1.4 siginfo-fault.c --- regress/sys/kern/siginfo-fault/siginfo-fault.c 13 Jul 2017 00:29:14 - 1.4 +++ regress/sys/kern/siginfo-fault/siginfo-fault.c 20 Jul 2017 16:02:42 - @@ -156,7 +156,7 @@ main() p[3] = 1; FAIL(); } - fail += checksig("mmap file", SIGBUS, BUS_ADRERR, p + 3); + fail += checksig("mmap file", SIGBUS, BUS_OBJERR, p + 3); return
Re: SIGBUS short mmap object
> Date: Mon, 17 Jul 2017 23:36:17 +0200 > From: Alexander Bluhm> > Hi, > > The tests regress/sys/kern/siginfo-fault accesses an mmap(2)ed file > behind its end. Then it expects an SIGBUS, but gets an SIGSEGV. As you found it, this was a bit controversial. Theo and I discussed this and did some software archeology. I didn't entirely convince him, but he gave in and won't object to something like this going in. We both agree that the SIGBUS/SIGSEGV dichotomy is a mess and I think that given that mess it makes sense to move the posts a bit and get to a situation where the BUS_XXX and SEGV_XXX codes make a bit more sense. > Related commit message is: > Additionally, SIGBUS/BUS_ADRERR should be generated instead of SIGSEGV > for access to file mapped pages that exceed the end of the file. > (Thanks to kettenis@ for suggesting this test.) > > I think this description refers to > http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html > Reference to whole pages within the mapping, but beyond the current > length of the object, shall result in a SIGBUS signal. It does. However, instead of SIGBUS/BUS_ADRERR it should SIGBUS/BUS_OBJERR. > The attached diff fixes that for amd64 and i386 and the test passes. > > ok? If you can make the BUS_ADRERR -> BUS_OBJERR change (both here and in the regress test), this is ok kettenis@ I'll make an effort to make sure the regress test passes on other architectures as well once this is in. > Index: arch/amd64/amd64/trap.c > === > RCS file: /data/mirror/openbsd/cvs/src/sys/arch/amd64/amd64/trap.c,v > retrieving revision 1.55 > diff -u -p -r1.55 trap.c > --- arch/amd64/amd64/trap.c 14 Jul 2017 12:20:32 - 1.55 > +++ arch/amd64/amd64/trap.c 14 Jul 2017 22:54:28 - > @@ -306,6 +306,7 @@ copyfault: > struct vm_map *map; > vm_prot_t ftype; > extern struct vm_map *kernel_map; > + int signal, sicode; > > cr2 = rcr2(); > KERNEL_LOCK(); > @@ -372,12 +373,14 @@ faultcommon: > map, fa, ftype, error); > goto we_re_toast; > } > + > + signal = SIGSEGV; > + sicode = SEGV_MAPERR; > if (error == ENOMEM) { > printf("UVM: pid %d (%s), uid %d killed:" > " out of swap\n", p->p_p->ps_pid, p->p_p->ps_comm, > p->p_ucred ? (int)p->p_ucred->cr_uid : -1); > - sv.sival_ptr = (void *)fa; > - trapsignal(p, SIGKILL, T_PAGEFLT, SEGV_MAPERR, sv); > + signal = SIGKILL; > } else { > #ifdef TRAP_SIGDEBUG > printf("pid %d (%s): %s at rip %llx addr %llx\n", > @@ -385,10 +388,15 @@ faultcommon: > frame->tf_rip, rcr2()); > frame_dump(frame); > #endif > - sv.sival_ptr = (void *)fa; > - trapsignal(p, SIGSEGV, T_PAGEFLT, > - error == EACCES ? SEGV_ACCERR : SEGV_MAPERR, sv); > } > + if (error == EACCES) > + sicode = SEGV_ACCERR; > + if (error == EIO) { > + signal = SIGBUS; > + sicode = BUS_ADRERR; > + } > + sv.sival_ptr = (void *)fa; > + trapsignal(p, signal, T_PAGEFLT, sicode, sv); > KERNEL_UNLOCK(); > break; > } > Index: arch/i386/i386/trap.c > === > RCS file: /data/mirror/openbsd/cvs/src/sys/arch/i386/i386/trap.c,v > retrieving revision 1.131 > diff -u -p -r1.131 trap.c > --- arch/i386/i386/trap.c 14 Jul 2017 12:20:32 - 1.131 > +++ arch/i386/i386/trap.c 14 Jul 2017 22:55:16 - > @@ -374,6 +374,7 @@ trap(struct trapframe *frame) > struct vmspace *vm; > struct vm_map *map; > int error; > + int signal, sicode; > > cr2 = rcr2(); > KERNEL_LOCK(); > @@ -431,9 +432,17 @@ trap(struct trapframe *frame) > map, va, ftype, error); > goto we_re_toast; > } > + > + signal = SIGSEGV; > + sicode = SEGV_MAPERR; > + if (error == EACCES) > + sicode = SEGV_ACCERR; > + if (error == EIO) { > + signal = SIGBUS; > + sicode = BUS_ADRERR; > + } > sv.sival_int = fa; > - trapsignal(p, SIGSEGV, vftype, > - error == EACCES ? SEGV_ACCERR : SEGV_MAPERR, sv); > + trapsignal(p, signal, vftype, sicode, sv); > KERNEL_UNLOCK(); > break; > } > Index:
Re: SIGBUS short mmap object
> From: Theo de Raadt> Date: Mon, 17 Jul 2017 16:06:05 -0600 (MDT) > > Sorry I disagree. SIGSEGV is the right answer. It was not mapped. > That is segmentation. Actually it is mapped in the sense that uvm has created mapping entries for it. The mapping just isn't backed (because the backing store was never there, or the file has been truncated in the meantime). > A SIGBUS should be delivered when the mode of access is not permitted > on a valid mapping. Most of our architectures (but not all) actually return SIGSEGV in that case. > I know some other operating systems have gotten extremly sloppy and > inconsistant in this regard. And I despise what they did. It suspect > they got so used to unaligned-access architectures they then forgot > the reason why SIGBUS was something else, and wanted to reuse it to > mean something else before siginfo became popular. And I think this > bug came about due to i386 X. OpenBSD is sloppy as well. The usual issue of importing from diffferent vintage NetBSD and FreeBSD is almost certainly at work here. The SIGBUS for mmap behaviour seems to be lifted straight from the SunOS 4.1.3 documentation: https://www.freebsd.org/cgi/man.cgi?query=mmap=0=2=SunOS+4.1.3=default=html Interestingly enough SunOS 5 "allows" both SIGBUS and SIGSEGV: https://www.freebsd.org/cgi/man.cgi?query=mmap=0=2=SunOS+5.10=default=html Pretty sure things started to diverge in an early stage. In the old days, SIGBUS and SIGSEGV simply mapped 1:1 to the VAX MMU traps. When different hardware came along with different MMU traps, the trap to signal mapping simply wasn't always obvious. Then of course the introduction of different vm systems caused further divergence. Your right that siginfo prompted folks to make further changes. Was that a SVR4 thing? > And if you quote POSIX, I think they are WRONG. It is irresponsible > of people to redefine the meanings of objects such that you cannot > tell what action to take to repair the problem. Dividing the possible fault scenarios in two classes is always going to lead to ambiguities. I suppose that is one of the reasons folks came up with siginfo. The competing BSD "solution" of using a sigcode that encoded the actual hardware trap is pretty much unusable for writing portable software. Here's what POSIX has to say about this: When an object is mapped, various application accesses to the mapped region may result in signals. In this context, SIGBUS is used to indicate an error using the mapped object, and SIGSEGV is used to indicate a protection violation or misuse of an address: * A mapping may be restricted to disallow some types of access. * Write attempts to memory that was mapped without write access, or any access to memory mapped PROT_NONE, shall result in a SIGSEGV signal. * References to unmapped addresses shall result in a SIGSEGV signal. * Reference to whole pages within the mapping, but beyond the current length of the object, shall result in a SIGBUS signal. * The size of the object is unaffected by access beyond the end of the object (even if a SIGBUS is not generated). I think this is a sensible way of distinguishing between SIGBUS and SIGSEGV. It just doesn't match historical practice in BSD land. And I can't blame them for following SVR4 here as in BSD land everything was (and still is) pretty much undocumented. That said, on many systems (including Linux) the siginfo implementation is incomplete and sometimes even incorrect. You cannot rely on it for writing portable code. But it is useful for debugging. The same holds for the mmap SIGBUS behaviour. The Solaris folks probably got it right when documenting that either SIGBUS or SIGSEGV could happen. Still I think that having more consistency between architectures on OpenBSD is a good thing. POSIX provides such a model, even if it deviates in places from historical behaviour. I'm not really worried about those deviations as we don't implement the historical behaviour consistently anyway. If you believe that siginfo is a useful thing, the POSIX interpretation of SIGBUS and SIGSEGV is the only one that really makes sense. In other words, I think we should go with bluhm@'s original diff. > Finally, the diff here only touches 2 architectures. Which others > change semantics in some odd way when that uvm diff goes in? Once we agree on the regression test for this, we can fix the other archiitectures.
Re: SIGBUS short mmap object
On Mon, Jul 17, 2017 at 04:06:05PM -0600, Theo de Raadt wrote: > Finally, the diff here only touches 2 architectures. Yes, I know. But why work on the others if the change is not accepted anyway. > Where is this coming from? Is it just to satisfy a regress test? My concern is only the regress test. I am totally fine to adapt the test instead of the kernel. But I don't know why the test was written that way. In general I think commiting tests for bugs or features someone else should fix later is a bad idea. This adapts the test to the current behavior. The SEGV_ACCERR looks wrong. Should I try to set SEGV_MAPERR in the kernel? bluhm Index: regress/sys/kern/siginfo-fault/siginfo-fault.c === RCS file: /data/mirror/openbsd/cvs/src/regress/sys/kern/siginfo-fault/siginfo-fault.c,v retrieving revision 1.4 diff -u -p -r1.4 siginfo-fault.c --- regress/sys/kern/siginfo-fault/siginfo-fault.c 13 Jul 2017 00:29:14 - 1.4 +++ regress/sys/kern/siginfo-fault/siginfo-fault.c 18 Jul 2017 09:23:57 - @@ -156,7 +156,7 @@ main() p[3] = 1; FAIL(); } - fail += checksig("mmap file", SIGBUS, BUS_ADRERR, p + 3); + fail += checksig("mmap file", SIGSEGV, SEGV_ACCERR, p + 3); return (fail); }
Re: SIGBUS short mmap object
The tests regress/sys/kern/siginfo-fault accesses an mmap(2)ed file behind its end. Then it expects an SIGBUS, but gets an SIGSEGV. Sorry I disagree. SIGSEGV is the right answer. It was not mapped. That is segmentation. A SIGBUS should be delivered when the mode of access is not permitted on a valid mapping. I know some other operating systems have gotten extremly sloppy and inconsistant in this regard. And I despise what they did. It suspect they got so used to unaligned-access architectures they then forgot the reason why SIGBUS was something else, and wanted to reuse it to mean something else before siginfo became popular. And I think this bug came about due to i386 X. And if you quote POSIX, I think they are WRONG. It is irresponsible of people to redefine the meanings of objects such that you cannot tell what action to take to repair the problem. Finally, the diff here only touches 2 architectures. Which others change semantics in some odd way when that uvm diff goes in? Where is this coming from? Is it just to satisfy a regress test? Related commit message is: Additionally, SIGBUS/BUS_ADRERR should be generated instead of SIGSEGV for access to file mapped pages that exceed the end of the file. (Thanks to kettenis@ for suggesting this test.) I think this description refers to http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html Reference to whole pages within the mapping, but beyond the current length of the object, shall result in a SIGBUS signal. The attached diff fixes that for amd64 and i386 and the test passes. ok? bluhm Index: arch/amd64/amd64/trap.c === RCS file: /data/mirror/openbsd/cvs/src/sys/arch/amd64/amd64/trap.c,v retrieving revision 1.55 diff -u -p -r1.55 trap.c --- arch/amd64/amd64/trap.c 14 Jul 2017 12:20:32 - 1.55 +++ arch/amd64/amd64/trap.c 14 Jul 2017 22:54:28 - @@ -306,6 +306,7 @@ copyfault: struct vm_map *map; vm_prot_t ftype; extern struct vm_map *kernel_map; + int signal, sicode; cr2 = rcr2(); KERNEL_LOCK(); @@ -372,12 +373,14 @@ faultcommon: map, fa, ftype, error); goto we_re_toast; } + + signal = SIGSEGV; + sicode = SEGV_MAPERR; if (error == ENOMEM) { printf("UVM: pid %d (%s), uid %d killed:" " out of swap\n", p->p_p->ps_pid, p->p_p->ps_comm, p->p_ucred ? (int)p->p_ucred->cr_uid : -1); - sv.sival_ptr = (void *)fa; - trapsignal(p, SIGKILL, T_PAGEFLT, SEGV_MAPERR, sv); + signal = SIGKILL; } else { #ifdef TRAP_SIGDEBUG printf("pid %d (%s): %s at rip %llx addr %llx\n", @@ -385,10 +388,15 @@ faultcommon: frame->tf_rip, rcr2()); frame_dump(frame); #endif - sv.sival_ptr = (void *)fa; - trapsignal(p, SIGSEGV, T_PAGEFLT, - error == EACCES ? SEGV_ACCERR : SEGV_MAPERR, sv); } + if (error == EACCES) + sicode = SEGV_ACCERR; + if (error == EIO) { + signal = SIGBUS; + sicode = BUS_ADRERR; + } + sv.sival_ptr = (void *)fa; + trapsignal(p, signal, T_PAGEFLT, sicode, sv); KERNEL_UNLOCK(); break; } Index: arch/i386/i386/trap.c === RCS file: /data/mirror/openbsd/cvs/src/sys/arch/i386/i386/trap.c,v retrieving revision 1.131 diff -u -p -r1.131 trap.c --- arch/i386/i386/trap.c 14 Jul 2017 12:20:32 - 1.131 +++ arch/i386/i386/trap.c 14 Jul 2017 22:55:16 - @@ -374,6 +374,7 @@ trap(struct trapframe *frame) struct vmspace *vm; struct vm_map *map; int error; + int signal, sicode; cr2 = rcr2(); KERNEL_LOCK(); @@ -431,9 +432,17 @@ trap(struct trapframe