The mailer of common-lisp.net dropped to the floor the previous transmission of the following message, so here it is again.
Cheers,
Jean-Claude Beaudoin
---------- Forwarded message ---------- From: Jean-Claude Beaudoin jean.claude.beaudoin@gmail.com Date: Wed, Mar 25, 2009 at 1:13 AM Subject: CFFI should defend itself against fragile handling of narrow return types in SBCL on x86! To: cffi-devel@common-lisp.net
Recent versions of GCC appear to be leaving spurious bits in the %eax register used to return the value of a C function on x86 based platforms when the return type is narrower than 32 bits (ie: short or char). SBCL does not defend itself against such register pollution and will return an incorrect value made up of those spurious bits in addition to the intended value. A partial fix for "signed" short and char has appeared in SBCL 1.0.25 but the "unsigned" cases are still vulnerable. I suggest that CFFI should defend itself against that dangerously permissive handling of narrow return types in SBCL through the use of a few filtering functions to be applied in those relevant cases, at least until SBCL fixes the issue for good.
Here is attached a patch against CFFI 0.10.4 with a version of the required code.
I also attach a set of test files that can be used to illustrate the problem.
Cheers,
Jean-Claude Beaudoin
2009/3/28 Jean-Claude Beaudoin jean.claude.beaudoin@gmail.com:
Recent versions of GCC appear to be leaving spurious bits in the %eax register used to return the value of a C function on x86 based platforms when the return type is narrower than 32 bits (ie: short or char).
Is that a GCC bug or is that behaviour here to stay?
I'd like to make this work for all the supported Lisps but only when necessary. Can we check at compile time whether this workaround is needed? For example, would the following work? Define abs() with a short/char return value and check whether the upper bits are masked away.
Hello Luis,
Sorry for the long silence, I was held up elsewhere.
On Mon, Mar 30, 2009 at 9:05 AM, Luís Oliveira luismbo@gmail.com wrote:
2009/3/28 Jean-Claude Beaudoin jean.claude.beaudoin@gmail.com:
Recent versions of GCC appear to be leaving spurious bits in the %eax register used to return the value of a C function on x86 based platforms when the return type is narrower than 32 bits (ie: short or char).
Is that a GCC bug or is that behaviour here to stay?
As far as GCC is concerned there is no bug of its own in that area. The oldest version of GCC I still have access to (3.4.6) shows that even back then GCC emitted code on the caller side that always extracted from %eax only the relevant bits and widened properly the result. I suspect that GCC has always done this although maybe not with the most efficient instruction sequence. What changed, starting in 4.1.X (I think), is that GCC started to use the upper part of %eax on the callee side trusting that this would not influence the caller side. I would even say that 4.3 started to use this behavior extensively.
I'd like to make this work for all the supported Lisps but only when necessary. Can we check at compile time whether this workaround is needed? For example, would the following work? Define abs() with a short/char return value and check whether the upper bits are masked away.
I just tried your idea and it works. Here is the code I used:
-------------------------------------------------- ;;~/bin/sbcl
(define-alien-routine ("abs" my-uabs) unsigned-char (i int :in)) (define-alien-routine ("abs" my-abs) char (i int :in))
(format t "~%(my-abs #x0101) = ~S~%" (my-abs #x0101)) (format t "~%(my-abs #x080) = ~S~%" (my-abs #x080)) (format t "~%(my-uabs #x0101) = ~S~%" (my-uabs #x0101)) (format t "~%(my-uabs #x181) = ~S~%" (my-uabs #x181))
(let ((val (my-uabs #x181))) (cond ((= #x81 val) (format t "unsigned-char widened properly.~%") ) ((= #x181 val) (format t "unsigned-char NOT widened properly.~%") ) ((> 0 val) (format t "unsigned-char got sign extended!~%") ) (t (format t "Stupefying result! val = ~D (#x~X).~%" val val)) ) )
;(quit) --------------------------------------------------------
The proper results should be 1, -128, 1, 129.
SBCL got partially fixed in 1.0.25 for the "signed char" and "signed short" case, and I submitted a patch to be hopefully included in 1.0.28 for the "unsigned" cases. Prior to these, every other version of SBCL out there has the weakness. By the way, CMUCL has the same problem.
Cheers,
Jean-Claude Beaudoin