Date: Fri, 2 Mar 2012 22:31:37 +0100 From: Tijl Coosemans <tijl@freebsd.org> To: Bruce Evans <brde@optusnet.com.au> Cc: svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org Subject: Re: svn commit: r232275 - in head/sys: amd64/include i386/include pc98/include x86/include Message-ID: <201203022231.43186.tijl@freebsd.org> In-Reply-To: <20120302132403.P929@besplex.bde.org> References: <201202282217.q1SMHrIk094780@svn.freebsd.org> <201203012347.32984.tijl@freebsd.org> <20120302132403.P929@besplex.bde.org>
next in thread | previous in thread | raw e-mail | index | archive | help
--nextPart4108460.GOc4c6QogL Content-Type: Text/Plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable On Friday 02 March 2012 05:11:21 Bruce Evans wrote: > On Thu, 1 Mar 2012, Tijl Coosemans wrote: >> Also, from ISO C: "All accessible objects have values, and all other >> components of the abstract machine [249] have state, as of the time the >> longjmp function was called" >> >> "[249] This includes, but is not limited to, the floating-point status >> flags and the state of open files." >> >> So I think storing mxcsr in jmp_buf is incorrect. >=20 > This is a well known bug in ISO C. ISO C never even tried to support > longjmp() from signal handlers, but we do. Supporting them requires > restoring significant parts of the FP environment. I fixed this for > the i387 control word in FreeBSD about 20 years ago. This was required > even to support float to integer conversions on i386. The situation > with these has changed a bit. It was: > - there is a default rounding mode. C didn't support changing it. It > is normally round-to-nearest. But for float to integer conversions, > it is round-towards-zero. To implement the latter, compilers switch > the mode to the latter mode. In old versions of FreeBSD, and still > with COMPAT4 signal handlers, handling of the FP state in signal > handlers was mostly incorrect. Signal handlers were passed the > current FP state, except for clobbering the exception flags for > SIGFPE's for hardware FP exceptions. Thus it was normal for signal > handlers to see the rounding mode switched to round-towards-zero. > This is not part of the abstract machine. A normal rounding mode > must be restored somehow. C90 didn't support changing the rounding > mode, so it would have been correct for C90 to hard-code the rounding > mode at the time of main() in signal handlers if you knew what that > was (it really should be set in crt or inherited across exec, instead > of being hard-coded in the kernel like it is in FreeBSD). But i387 > supports changing the rounding mode. It is simplest to restore it > to that at the time of the setjmp(). > Now, things are even more complicated: > - signal handlers are normally passed a clean FP state. (Since C > barely supports signal handlers, it doesn't say anything about this). > Now, longjmp() from a signal handler would return this clean state > if no FP state is restored, unless the signal handler does some FP > operations that dirty its clean state. Returning the clean state > has much the same effect in simple cases as restoring the state at > the time of the setjmp(), because nothing except the compiler doing > the float to integer conversions changes the state from its default, > and the longjmp() takes us to a point where the compiler is not doing > these conversions so it is correct for the normal state to be > restored. There is now the minor simplication that i386 with SSE > doesn't need the mode switch for floats, and i386 with SSE2 doesn't > need it for doubles; but i386 still needs it for long doubles. SSE3 also added FISTTP instruction that converts to integer with truncation. > There > is the minor complication that the signal handler may be COMPAT4, > in which case its FP state is not clean and the old method must be > used -- longjmp() can hardly be expected to tell which type the > signal handler is and adjust its behaviour to match. > - C now supports changing the rounding mode. Its requirement that > longjmp() not restore the previous rounding mode may be correct for > some cases, but it is broken for longjmp() out of signal handlers: > - suppose the signal handler gets a clean state, as in FreeBSD. Then > any longjmp() out of a signal handler that doesn't restore the > rounding mode (or any other part of the FP env) resets to the clean > state (which should be the same as the default state); this state > may differ from the state at the time of the setjmp() and also from > the state at the time of the signal. This is broken. Perhaps the > - suppose the signal handler doesn't get a clean state. Who knows > what it is? Standards don't specify this. Even if the signal > handler understands everything, then it will have a hard time > cleaning up the state so that it is right at the time of the > longjmp(). Note that it is not just SIGFPE handlers for hardware > FP exceptions that would need to understand everything about FP > to do the right thing. _All_ signal handlers would need this, > since for example a harmless SIGINT handler might be interrupting > a FP operation that changes the FP env in ways outside of the > abstract machine. >=20 > Next, there are the FP exception flags. C90 doesn't support these, and > I didn't worry about these 20 years ago. I just put the i386 FP control > word in jmp_buf, and used fninit to clean out everything else in the > FP env. Now, C99 supports these. These should not be changed by > longjmp(). However, for the case of longjmp() from a signal handler, > if nothing is restored, then all of them will be be cleared by the > longjmp() in the usual case where the signal handler doesn't dirty > its clean state. Worse, if the signal handler dirties it state and > doesn't do this intentionally to prepare for the longjmp(), then the > main part of the program gets its exception flags replaced by the > signal handler. Again, it is very difficult for signal handlers to > understand FP well enough to do the right thing. SIGFPE ones have to > understand a little more here. They have to understand that the kernel > doesn't understand this stuff, so it has destroyed the exception flags > in the saved state after only making a lossy copy of them (in the > signal code). Destruction of the exception flags allows the case of > returning from a signal handler to sort of work (the SIGFPE doesn't > repeat). This problem only occurs if signals for FP exceptions are > unmasked. Otherwise, SIGFPE never occurs for FP exceptions, but only > for integer exceptions like division by 0. >=20 > The amd64 _setjmp.S and setjmp.S (but not its sigsetjmp.S) save mxcsr > in setjmp but only restores the non-flags from it in longjmp; it loses > the fninit (except in sigsetjmp.S): >=20 > % setjmp: > % fnstcw 64(%rcx) /* 8; fpu cw */ > % stmxcsr 68(%rcx) /* and mxcsr */ > % longjmp: > % /* Restore the mxcsr, but leave exception flags intact. */ > % stmxcsr -4(%rsp) > % movl 68(%rdx),%eax > % andl $0xffffffc0,%eax > % movl -4(%rsp),%edi > % andl $0x3f,%edi > % xorl %eax,%edi > % movl %edi,-4(%rsp) > % ldmxcsr -4(%rsp) > % ... > % // lost fninit here > % fldcw 64(%rdx) >=20 > For longjmp() from signal handlers, leaving the exception flags intact > is worse than useless, since these are only the signal handler's > exception flags (it would be better to clear them). >=20 > On i386, the bugs are similar, except the mxcsr is not touched: > - fninit has been removed from _setjmp.S and setjmp.S > - sigsetjmp.S has not been touched (so it is missing mxcsr handling, > but still does fninit). >=20 > Removing the fninits is very large breakage in the i386 case. amd64 > doesn't support COMPAT4 signal handlers. Thus its signal handlers > start with a clean state and fninit in them only cleans up any dirt > made by the signal handler, and there is rarely even minor dirt. > But for i386 with a COMPAT4 signal handler, when a signal interrupts > an FP operation, the i387 FP stack always has something on it. > This needs to be cleaned before or by longmp() if longjmp() is used > to quit the signal handler. Without COMPAT4 signal handlers, the > only obvious bug is that leaving the i387 exception flags intact is > worse than useless, the same as for then the sr part of mxcsr. >=20 > Other things in the FP env are less portable so they are less used so > they cause fewer problems. Standards don't support many other things, > so the implementation can be correct for them even if the standard > requires brokenness for other parts of the env. i387 precision control > is an example. Thanks, that was quite informative. C11 does say something about the =46P env and signals now though: ``When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects that are neither lock-free atomic objects nor of type volatile sig_atomic_t are unspecified, as is the state of the floating-point environment. The value of any object modified by the handler that is neither a lock-free atomic object nor of type volatile sig_atomic_t becomes indeterminate when the handler exits, as does the state of the floating-point environment if it is modified by the handler and not restored to its original state.'' This means a signal handler must not rely on the state of the FP env. It may install its own FP env if needed (e.g. FE_DFL_ENV), but then it must restore the original before returning. This allows for the rounding mode to be silently modified for integer conversions for instance. If longjmp is not supposed to change the FP env then, when called from a signal handler, either the signal handler must install a proper FP env before calling longjmp or a proper FP env must be installed after the target setjmp call. Otherwise the FP env is unspecified. --nextPart4108460.GOc4c6QogL Content-Type: application/pgp-signature; name=signature.asc Content-Description: This is a digitally signed message part. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.18 (FreeBSD) iF0EABEIAAYFAk9RPD8ACgkQfoCS2CCgtisf9AD6AwgLOyAdGJXp+BuruSn2PgXn n2mW1jTiRinSLXryIlgA+IotQDuOIJlWuzaf+nfFTc6KWGuEagdZrhSjCMaDLJE= =Soep -----END PGP SIGNATURE----- --nextPart4108460.GOc4c6QogL--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201203022231.43186.tijl>