This is the mail archive of the newlib@sourceware.org mailing list for the newlib project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RE: [PATCH] Fix undefined behaviour in lround,llround.


Dave:
 
A small suggestion for the macros (not a problem as used now,
but paranoia for future):
-(((amt) < 8 * sizeof(op)) ? (op << amt) : 0)
+(((amt) < 8 * sizeof(op)) ? ((op) << (amt)) : 0)
-(((amt) < 8 * sizeof(op)) ? (op >> amt) : 0)
+(((amt) < 8 * sizeof(op)) ? ((op) >> (amt)) : 0)
 
Changes look good to me, but, yes, they are over-cautious in
applying the macro, as they are only needed in one place each in the
files--the first one.  In the final if (two shifts inside),
20 <= exponent_less_1023 <= 51, so the possible shift amounts are only
0 to 31, not needing the protection.
 
The attached revised patch has changes for both of these for your
consideration.
 
I think that this error is likely in more files (lrint is likely),
as there are many similarites.  (I'll have to take a look later.)
 
Craig



-----Original Message-----
From: newlib-owner@sourceware.org on behalf of Dave Korn
Sent: Fri 7/16/2010 12:13 PM
To: newlib@sourceware.org
Subject: [PATCH] Fix undefined behaviour in lround,llround.
 

    Hi all,

  Back at the start of June we had a report(*) of a bug in the gfortran
nint()
function, which I tracked down to a problem in the underlying
implementation
of lround() in newlib.  The bug occurs here:

>   else if (exponent_less_1023 < (8 * sizeof (long int)) - 1)
>     {
>       if (exponent_less_1023 >= 52)
>         result = ((long int) msw << (exponent_less_1023 - 20)) | (lsw
<< (exponent_less_1023 - 52));
>       else
>         {
>           unsigned int tmp = lsw + (0x80000000 >> (exponent_less_1023
- 20));
>           if (tmp < lsw)
>             ++msw;
>           result = ((long int) msw << (exponent_less_1023 - 20)) |
(tmp >> (52 - exponent_less_1023));
>         }
>     }
>   else

  When exponent_less_1023 is exactly 52, the subexpression "((long int)
msw <<
(exponent_less_1023 - 20))" becomes a left shift by 32.  On systems
where a
long int is 32 bits, that's undefined behaviour; the shift amount must
always
be less than the size of the left-hand operand (n1256, 6.5.7#3), and in
practice, this means that a standard x86 shift opcode is used, which has
the
property of implicitly AND-ing the shift amount with 31; the result of a
shift
by 32 bits thus isn't zero as the code expects, but the original
unshifted
value of msw, which gets OR-d into completely the wrong place and splots
bits
all over the correctly-shifted lsw part of the result.

  In this patch I define a couple of safe shift macros that return zero
and
avoid the undefined behaviour when the shift amount is too great.  I've
actually been slightly over-cautious in applying it a couple of places,
I
think, but this isn't the fast math code and anyway value-range analysis
is a
job for a compiler, which it can do bearing in mind the actual type
sizes in
effect during compilation.

newlib/ChangeLog:

	* libm/common/fdlibm.h (SAFE_LEFT_SHIFT): New macro definition.
	(SAFE_RIGHT_SHIFT): Likewise.
	* libm/common/s_llround.c (llround): Use them to avoid undefined
	behaviour.
	* libm/common/s_lround.c (lround): Likewise.

  OK?

    cheers,
      DaveK
-- 
(*) - http://cygwin.com/ml/cygwin/2010-06/threads.html#00315


Attachment: fpmath-lround-fix2.diff
Description: fpmath-lround-fix2.diff


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]