NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

lib/44449: vfprintf() prints incorrect negative float value for GCC-4.5



>Number:         44449
>Category:       lib
>Synopsis:       vfprintf() prints incorrect negative float value for GCC-4.5
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    lib-bug-people
>State:          open
>Class:          support
>Submitter-Id:   net
>Arrival-Date:   Mon Jan 24 07:25:00 +0000 2011
>Originator:     Amol Pise
>Release:        NetBsd 4.0
>Organization:
Tata Elxsi limited
>Environment:
Linux 10.4.3.29 2.6.29.6-rt24-alp_nl-beagleboard #2 PREEMPT Mon Jan 17 15:44:46 
IST 2011 armv7l GNU/Linux
>Description:

I have found one issue in stdio module of NetBSD library while compiling it for 
ARMv7a (Cortex architecture) using the GNU GCC-4.5 toolchain.

The issue is while printing negative float value and the float value 
greater than 9.0, vfprintf() prints malicious characters.

Later, I modified the optimization flag from O2 to O1 and build the NetBsd 
library and found the issue is resolved.

On these lines further I have  analyzed and suspected this ?wrong optimization? 
may be cause due to ?strict aliasing?

The man page of GNU GCC says about ?strict aliasing?:

?-fstrict-aliasing
           Allows the compiler to assume the strictest aliasing rules 
applicable to the language being compiled. For C (and C++), this activates 
optimizations based on the type of expressions.  In particular, an object of 
one type is assumed never to reside at the same address as an object of a 
different type, unless the types are almost the same.  For example, an 
"unsigned int" can alias an "int", but not a "void*" or a "double".  A 
character type may alias any other type. ?


To confirm this I have build NetBsd library using  ?-fno-strict-aliasing?  flag 
with O2 optimization to suppress the ?strict aliasing? and found the above 
issue is resolved.


Further I have investigated it in the NetBsd source and found the flow of 
vfprintf as below:

 Vfprintf()
    ---> __vfprintf_unlocked
        ---> cvt()
           ---> __dtoa
            

The dtoa() implementation in NetBsd as follows:

$cat src/lib/libc/gdtoa/dtoa.c
{{{

148:        if (word0(d) & Sign_bit) {
149:                /* set sign for everything, including 0's and NaNs */
150:                *sign = 1;
151:                word0(d) &= ~Sign_bit;  /* clear sign bit */
152:                }

}}}

$cat src/lib/libc/gdtoa/gdtoaimp.h
{{{
176: #define ULong  uint32_t
:
278:
279: typedef union { double d; ULong L[2]; } U;
:
291: #ifdef IEEE_LITTLE_ENDIAN
292: #define word0(x) ( /* LINTED */ (U*)&x)->L[1]
293: #define word1(x) ( /* LINTED */ (U*)&x)->L[0]

}}}

Since at line:292 its breaking the strict aliasing rule. Cuases floating point 
number convetion fails.




>How-To-Repeat:
This issue can be reproduce using the sample test as below:

% Cat Sample_vfprintf.c
{{{

#include <stdio.h>
#include <stdarg.h>


void format (FILE * stream, char * format, ...)
{
  va_list args;
  va_start (args, format);
  vfprintf (stream, format, args);
  va_end (args);
}


int main ()
{
   FILE * fp;

   fp = fopen ("myfile.txt","w");
   format (fp,"Call with %f \n",-3.14);
   format (fp,"Call with %f \n",10.0);

   fclose (fp);
   return 0;
}

}}}


The output on beagleboard target is:
{{{
-bash-3.2# ./sample_vfprintf

-bash-3.2# cat myfile.txt 

Call with 0.,0000 
Call with :.000000 
}}}

>From the above sample it is seen that for negative float value and the float 
>value greater than 9.0, vfprintf() prints malicious characters.


>Fix:

To bypass this strict aliasing rules I used ?__may_alias__? attribute as below 
(This is the workaround fix).

$cat src/lib/libc/gdtoa/gdtoaimp.h
{{{
- typedef union { double d; ULong L[2]; } U;
+ typedef union { double d; ULong L[2]; } __attribute__((__may_alias__)) U;

}}}

With these changes vfprintf() is able to convert floating point number 
correctly without any issue.




Home | Main Index | Thread Index | Old Index