Bug 4 - lround() result usually bad in range [1048576,2097152)
Summary: lround() result usually bad in range [1048576,2097152)
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: math (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Andreas Jaeger
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-01-30 20:09 UTC by Richard Kelly
Modified: 2020-06-27 12:37 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Last reconfirmed: 2004-01-30 20:35:21
fweimer: security-


Attachments
Testprogram (142 bytes, text/plain)
2004-01-30 20:34 UTC, Andreas Jaeger
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Richard Kelly 2004-01-30 20:09:26 UTC
>Confidential:    no
>Synopsis:    lround() result usually bad in range [1048576,2097152)
>Severity:    serious
>Priority:    medium
>Category:    libc
>Class:        sw-bug
>Release:    libc-2.3.2
>Environment:
    
Host type: i386-redhat-linux-gnu
System: Linux schween.jpl.nasa.gov 2.4.20-28.9 #1 Thu Dec 18 13:45:22 EST 2003
i686 i686 i386 GNU/Linux
Architecture: i686

Addons: linuxthreads c_stubs glibc-compat
Build CFLAGS: -march=i386 -DNDEBUG=1 -finline-limit=2000 -g -O3
Build CC: gcc
Compiler version: 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
Kernel headers: 2.4.20
Symbol versioning: yes
Build static: yes
Build shared: yes
Build pic-default: no
Build profile: yes
Build omitfp: no
Build bounded: no
Build static-nss: no

>Description:
    lround() produces garbage for nearly all arguments greater
    than or equal to 2**20 but less than 2**21.  The problem
    occurs because lround() attempts to right shift a 32-bit
    unsigned integer by 32 bits, and that operation is undefined
    according to the standard.
    
    Within that range, some rare arguments, e.g. integers plus
    one half, do actually lead to correct results, at least on
    this machine.  That's because the erroneous shift's result
    just happens to be right for those bit patterns.
    
    The problem appears in sysdeps/ieee754/dbl-64/s_lround.c, on
    line 61, which is excerpted below:
    
        result = ((long int) i0 << (j0 - 20)) | (j >> (52 - j0));
                                                 ^^^^^^^^^^^^^^
                                                    BAD SHIFT
    
    In our case, j is a uint32_t, and j0 is 20, which makes the
    right shift undefined.

>How-To-Repeat:
    long int j = lround( 1071930.0008 );
    /* j should be 1071930, but instead is large neg value. */

>Fix:
    Change lround() code to handle this case, so it doesn't do
    the bad shift. There are several system-dependent variations
    of the s_lround.c file.  We don't know if the other variations
    are right or wrong.

    Sibling file s_llround.c (long long int version) does check for
    the j0==20 case, so it's probably okay.
Comment 1 Andreas Jaeger 2004-01-30 20:34:38 UTC
Created attachment 1 [details]
Testprogram
Comment 2 Andreas Jaeger 2004-01-30 20:35:21 UTC
Ok, verified with the programm t.c:

gromit:~/tmp:[0]$ gcc -Wall t.c -lm
gromit:~/tmp:[0]$ ./a.out 
2150924286
Comment 3 Andreas Jaeger 2004-02-01 19:53:14 UTC
Fixed with a patch with the description below, it will be in the next release.

Thanks,
Andreas

2004-02-01  Ulrich Drepper  <drepper@redhat.com>

        * math/libm-test.inc (lround_test): Add new test.
        (llround_test): Likewise.
        (lrint_test): Likewise.
        (llrint_test): Likewise.
        * sysdeps/ieee754/dbl-64/s_lround.c (__lround): Fix special case
        with result taking up 20 bits.
        * sysdeps/ieee754/dbl-64/s_lrint.c (__lrint): Likewise.
        * sysdeps/ieee754/dbl-64/s_llrint.c (__llrint): Likewise..
        * sysdeps/ieee754/ldbl-96/s_lroundl.c (__lroundl): Fix special
        case with result taking up 31 bits.
        * sysdeps/ieee754/ldbl-96/s_lrintl.c (__lrintl): Likewise.
Comment 4 footest"><svg>< 2020-06-27 12:37:20 UTC
Comment on attachment 1 [details]
Testprogram

>#define _GNU_SOURCE 1
>#include <math.h>
>#include <stdio.h>
>
>int
>main (void)
>{
>  long int j = lround (1071930.0008);
>
>  printf ("%ld\n", j);
>
>  return 0;
>}