Bug 272229 - printf produces bogus output when printing double 0x00000000000000ff with %g
Summary: printf produces bogus output when printing double 0x00000000000000ff with %g
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: arm (show other bugs)
Version: 13.1-RELEASE
Hardware: Any Any
: --- Affects Many People
Assignee: Michal Meloun
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-06-26 19:09 UTC by Robert Clausecker
Modified: 2024-07-31 17:44 UTC (History)
4 users (show)

See Also:
fuz: mfc-stable13?
fuz: mfc-stable12?


Attachments
regenerate gdtoa headers (1.20 KB, patch)
2023-06-28 10:48 UTC, John F. Carr
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Robert Clausecker freebsd_committer freebsd_triage 2023-06-26 19:09:38 UTC
Consider this test program:

#include <stdio.h>


int main()
{
        union intfloat {
                int i;
                double f;
        } intf;

        intf.i = 255;

        printf("%g\n", intf.f);
}

This should print (e.g. on aarch64):

    $ cc -o bug bug.c
    $ ./bug
    1.25987e-32

But on armv7 we get instead:

    $ ./bug
    e-309

Furthermore, there is an embedded NUL byte in the output:

    $ ./bug | od -c                                                                                   
    0000000   \0   e   -   3   0   9  \n                                    
    0000007

Preliminary debugging suggests that this could be because dtoa() returns an empty string when vfprintf attempts the conversion.  Further debugging is hindered as gdb cannot find debug symbols for dtoa (it seems to expect a source file of different name than the actual file).

Among other things, this error causes devel/orc to fail during build.
Comment 1 Robert Clausecker freebsd_committer freebsd_triage 2023-06-26 19:38:13 UTC
Here's a simpler reproducer:

    #include <stdio.h>

    int main()
    {
            printf("%g\n", 1.26e-321);
    }

This works fine on i386, amd64, and aarch64 but fails identically on armv7.
Comment 2 John F. Carr 2023-06-26 20:19:57 UTC
The test program as written includes 4 uninitialized bytes in the double value on a 32 bit system.  Forcing those bytes to zero using

    memset(&intf, 0, sizeof intf);

before assigning to the int member results in the described output.
Comment 3 Robert Clausecker freebsd_committer freebsd_triage 2023-06-26 21:55:49 UTC
(In reply to John F. Carr from comment #2)

The bug also happens without the union, see the simpler reproducer in comment #1.
Comment 4 commit-hook freebsd_committer freebsd_triage 2023-06-26 23:30:59 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/ports/commit/?id=cb61cd8b665eccd6764df0ecae01dcaa4b8f5b5f

commit cb61cd8b665eccd6764df0ecae01dcaa4b8f5b5f
Author:     Robert Clausecker <fuz@FreeBSD.org>
AuthorDate: 2023-06-26 19:43:25 +0000
Commit:     Robert Clausecker <fuz@FreeBSD.org>
CommitDate: 2023-06-26 23:30:29 +0000

    devel/orc: work around armv7 libc bug

    The armv7 libc has a bug in printf() where printing a denormal double
    may cause NUL bytes to be written out.  This port tries to write a
    diagnostic comment containing such a denormal to a C source file.  The
    bug causes the source file to be malformed, breaking the port and its
    consumers.

    Work around the bug by killing the offending part of the diagnostic
    comment.

    Approved by:    portmgr (build fix blanket)
    Reviewed by:    diizzy
    PR:             272229

 devel/orc/Makefile                             |  1 +
 devel/orc/files/patch-orc_orcprogram-c.c (new) | 19 +++++++++++++++++++
 2 files changed, 20 insertions(+)
Comment 5 John F. Carr 2023-06-27 00:09:33 UTC
The file with __dtoa() is /usr/src/contrib/gdtoa/dtoa.c.  It is compiled under the name gdtoa_dtoa.c.  See lib/libc/gdtoa/Makefile.inc.

For a more direct test, skipping printf, I used

#include <stdio.h>

extern char *__dtoa(double d0, int mode, int ndigits, int *decpt, int *sign, char **rve);

int main(int argc, char *argv[])
{
  double d = 1.26e-321;
  int decpt = 0, sign = 0;
  char *converted = __dtoa(d, 2, 6, &decpt, &sign, 0);

  if (converted)
    fprintf(stdout, "sign = %d decpt = %d text = %s\n",
            sign, decpt, converted);

  return ferror(stdout);
}

I got the bad result on FreeBSD 13 and CURRENT, both in an armv7 chroot on an arm64 host.
Comment 6 Michal Meloun freebsd_committer freebsd_triage 2023-06-28 04:17:17 UTC
It seems that libc/arm/gd_qnan.h is wrong (or at least qnan config tool gives different values). Also libc/arm/arith.h is probably suboptimal...
Comment 7 John F. Carr 2023-06-28 10:48:14 UTC
Created attachment 243053 [details]
regenerate gdtoa headers
Comment 8 John F. Carr 2023-06-28 10:50:30 UTC
(In reply to Michal Meloun from comment #6)

gd_qnan.h and arith.h were both wrong.  The bug was caused by arith.h defining Sudden_Underflow.

The test program works with the patch I attached.

I can not say whether this patch is appropriate for armv6 or for software floating point.  Apparently CURRENT only supports little-endian with hardware floating point.
Comment 9 Robert Clausecker freebsd_committer freebsd_triage 2023-06-30 08:12:27 UTC
I can confirm that attachment #243053 [details] fixes the bug.
Comment 10 Robert Clausecker freebsd_committer freebsd_triage 2023-08-08 15:43:15 UTC
@mmel any progress on getting this committed?  It would be great if we could get this in for 14.0.
Comment 11 commit-hook freebsd_committer freebsd_triage 2023-08-13 07:36:15 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=e59b6e48f4c8bbfee43a1bc6d9fd47691d3bd603

commit e59b6e48f4c8bbfee43a1bc6d9fd47691d3bd603
Author:     Michal Meloun <mmel@FreeBSD.org>
AuthorDate: 2023-08-13 05:51:56 +0000
Commit:     Michal Meloun <mmel@FreeBSD.org>
CommitDate: 2023-08-13 07:35:48 +0000

    gdtoa: Regenerate configuration headers for 32-bit arm.

    These configuration headers were only guessed, but unfortunately not exactly
    correctly. Therefore, re-generate them on real HW.

    Generated on CA15, verified on CA9, CA7(with VFP lite) and on 32-bit ARMv9.

    PR:     272229
    Reported by:    Robert Clausecker <fuz@FreeBSD.org>
    MFC after:      2 weeks

 lib/libc/arm/arith.h   |  9 ++++-----
 lib/libc/arm/gd_qnan.h | 11 +++++------
 2 files changed, 9 insertions(+), 11 deletions(-)
Comment 12 Robert Clausecker freebsd_committer freebsd_triage 2023-09-12 05:37:38 UTC
Could you please MFC this one?  It should go into releng/14.0, too.  Broken printf is really quite bad.
Comment 13 commit-hook freebsd_committer freebsd_triage 2024-07-31 17:24:27 UTC
A commit in branch stable/13 references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=8ca43e674e3cddf3574e062abde6cd70c120984e

commit 8ca43e674e3cddf3574e062abde6cd70c120984e
Author:     Michal Meloun <mmel@FreeBSD.org>
AuthorDate: 2023-08-13 05:51:56 +0000
Commit:     Michal Meloun <mmel@FreeBSD.org>
CommitDate: 2024-07-31 12:25:35 +0000

    gdtoa: Regenerate configuration headers for 32-bit arm.

    These configuration headers were only guessed, but unfortunately not exactly
    correctly. Therefore, re-generate them on real HW.

    Generated on CA15, verified on CA9, CA7(with VFP lite) and on 32-bit ARMv9.

    PR:     272229
    Reported by:    Robert Clausecker <fuz@FreeBSD.org>
    MFC after:      2 weeks

    (cherry picked from commit e59b6e48f4c8bbfee43a1bc6d9fd47691d3bd603)

 lib/libc/arm/arith.h   | 10 ++++++----
 lib/libc/arm/gd_qnan.h | 11 ++++++-----
 2 files changed, 12 insertions(+), 9 deletions(-)