printf function outputs different results for same arguments

Bug #2012076 reported by Stan Liao
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
glibc (Ubuntu)
Invalid
Undecided
Unassigned

Bug Description

A union is defined for storing different datatype values; and all values' data lengths are the same.

Since the data lengths of the union members are the same, i suppose that printf function can have specified output format with specifying any union member.

The scenario can be seen in the following sample code. Variable v is assigned with a floating number in the beginning; to output this value, the output format is specified as "%f" in the printf function format string, but the value is given by v.p, which is a void * type, as next argument. See the output results of the printf("v.p_f = %f\n", v.p); statement, there are 3 different result.

//===========================
#include <stdio.h>
#include <stdlib.h>

typedef union
{
    double f;
    long int i;
    void *p;
} type_t;

void func2(int a, int b, type_t v);

int main(void)
{
    type_t v;
    unsigned char *p;

    printf("sizeof(double) = %ld\n", sizeof(double));
    printf("sizeof(long int) = %ld\n", sizeof(long int));
    printf("sizeof(void *) = %ld\n", sizeof(void *));
    printf("sizeof(type_t) = %ld\n", sizeof(type_t));

    v.f = 123.456789;

    printf("\nIn %s()\n", __FUNCTION__);
    printf("v.p_f = %f\n", v.p);
    printf("v.i_x = %lX\n", v.i);

    printf("cont. = ");
    p = (unsigned char *) &v;
    for (int i = 7; i >= 0; --i)
        printf("%02X", p[i]);
    printf("\n");

    printf("v.p_p = %p\n", v.p);
    printf("v.i_d = %ld\n", v.i);
    printf("v.f_f = %f\n", v.f);
    printf("v.p_f = %f\n", v.p);

    func2(1, 2, v);

    printf("\nIn %s()\n", __FUNCTION__);
    printf("v.i_d = %ld\n", v.i);
    printf("v.p_f = %f\n", v.p);
    printf("v.f_f = %f\n", v.f);
    printf("v.p_f = %f\n", v.p);
    printf("\n");

#ifdef __GNUC__
    printf("__GNUC__\n");
    printf("__VERSION__ = %s\n", __VERSION__);
#endif

    fflush(stdout);

#ifdef __linux__
    system("`find /usr/lib -executable -name *libc.so*` --version");
    printf("\n");
#endif
    return (0);
}

void func2(int a, int b, type_t v)
{
    unsigned char *p;

    printf("\nIn %s()\n", __FUNCTION__);
    printf("v.p_f = %f\n", v.p);
    printf("v.i_x = %lX\n", v.i);

    printf("cont. = ");
    p = (unsigned char *) &v;
    for (int i = 7; i >= 0; --i)
        printf("%02X", p[i]);
    printf("\n");

    printf("v.p_p = %p\n", v.p);
    printf("v.i_d = %ld\n", v.i);
    printf("v.f_f = %f\n", v.f);
    printf("v.p_f = %f\n", v.p);
}

//==================================
sizeof(double) = 8
sizeof(long int) = 8
sizeof(void *) = 8
sizeof(type_t) = 8

In main()
v.p_f = 0.000000
v.i_x = 405EDD3C07EE0B0B
cont. = 405EDD3C07EE0B0B
v.p_p = 0x405edd3c07ee0b0b
v.i_d = 4638387916139006731
v.f_f = 123.456789
v.p_f = 123.456789

In func2()
v.p_f = 0.000000
v.i_x = 405EDD3C07EE0B0B
cont. = 405EDD3C07EE0B0B
v.p_p = 0x405edd3c07ee0b0b
v.i_d = 4638387916139006731
v.f_f = 123.456789
v.p_f = 123.456789

In main()
v.i_d = 4638387916139006731
v.p_f = -5486124068793688683255936251187209270074392635932332070112001988456197381759672947165175699536362793613284725337872111744958183862744647903224103718245670299614498700710006264535590197791934024641512541262359795191593953928908168990292758500391456212260452596575509589842140073806143686060649302051520512.000000
v.f_f = 123.456789
v.p_f = 123.456789

__GNUC__
__VERSION__ = 9.4.0
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 9.4.0.
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.

Revision history for this message
Simon Chopin (schopin) wrote :

Hi,

You're invoking undefined behaviour in your examples by providing non-double arguments for a %f conversion specifier. In particular, when you give a pointer rather than a double, GCC completely discards the argument, as illustrated in this example (see eax set to 0 in first printf call, 1 to the other ones):

https://godbolt.org/z/1EcxK9T53

If you cast the argument appropriately, it prints the correct value.

The relevant section in the C17 specification is 7.21.6.1, particularly paragraph 9:

> If a conversion specification is invalid, the behavior is undefined.286) If any argument is not the
correct type for the corresponding conversion specification, the behavior is undefined.

Closing this as Invalid.

Changed in glibc (Ubuntu):
status: New → Invalid
Revision history for this message
Stan Liao (stanliao) wrote :

From the viewpoint that "%f" directs printf to retrieve a 8-byte data from stack, and the function call statement does push a 8-byte floating-point number into stack (though the statement says that the data is a pointer), then printf shall work correctly. Ya, there is no such issue if the program is compiled by Microsoft VC compiler. So, the glibc library does additional checks. Thanks a lot.

Revision history for this message
Simon Chopin (schopin) wrote : Re: [Bug 2012076] Re: printf function outputs different results for same arguments

Note that the issue is actually not with the glibc printf
implementation, but directly in the compiler, which knows about the
details of printf specification, notably in order to give you proper
warnings, and chooses not to add the problematic argument.

I'm guessing that if you were to use a "dumber" compiler for your code
but still linked against glibc it could result in the same thing as
you're seeing with MSVC.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.