gcc feature breaks glibc feature

most gnome hackers are probably accustomed to the fact that they can pass a null pointer as a value to glibc’s “%s” conversion character and get the string “(null)” output instead of a crash.

take for example, this program:

#include <stdio.h>

int
main (void)
{
  printf ("%s", NULL);

  return 0;
}

this will output “(null)”. nice. i like this glibc feature.

of course, this program fails to put a newline. let’s make the obvious fix:

#include <stdio.h>

int
main (void)
{
  printf ("%s\n", NULL);

  return 0;
}

this program segfaults.

why is this?

let’s look at the assembly code generated for the second program:

...
...
main:
        ....
        ....
        call puts
        ....

it turns out that if gcc sees “printf (“%s\n”, string);” then it assumes that this is exactly equivalent to “puts (string);” and emits the puts code instead. this is without any optimisation enabled. of course, compiling with -ffreestanding causes it to not make this assumption.

of course, puts will crash if you give it a null pointer.

i guess the assumption is probably valid by strict reading of the relevant specifications (printfing a null string is probably said to be “undefined”) but clearly this feature of gcc is in conflict with the “(null)” feature of glibc.

11 Comments

  1. Posted October 1, 2007 at 5:00 am | Permalink

    While not a perfect solution, doing ” %s” instead of “%s” is enough.

  2. rwg
    Posted October 1, 2007 at 5:50 am | Permalink

    Unfortunately, the gcc folks seem unwilling to fix this since they say the results of printf(“%s\n”, NULL) are undefined:

    http://gcc.gnu.org/bugzilla/show_bug.cgi?id=15685

    (It would be nice to see consistency between gcc and glibc.)

    It looks like your choices are -fno-builtin-printf or printf(“%s\n”, s ? s : “(null)”);

  3. Matt Walton
    Posted October 1, 2007 at 6:38 am | Permalink

    Surely you should be using printf(“%s\n”, s ? s : “(null)”); anyway? Relying on that feature of glibc is going to bite you next time you need to compile something against a C library which doesn’t behave like that.

    It’s also possible it might encourage a cavalier attitude towards null pointers which is not reflected in other parts of the library or other libraries, leading to a lack of care and greater risk of segfaults. One thing programming commercially does teach is that too many people don’t pay attention to their pointers being null.

    That said, by spec, is the optimisation valid in the defined behavioural cases? Best not to complain to the implementers about undefined ones – the language/library spec is at fault there.

    Wouldn’t it be nice to live in a world where there were no undefined cases in C…

  4. Aivars
    Posted October 1, 2007 at 8:04 am | Permalink

    Printing NULL is a common crasher on SunOS and looks like AIX and HP-UX treat NULL as zero length string.
    I agree with Matt that programmers must check for NULL pointers.

  5. Posted October 1, 2007 at 8:05 am | Permalink

    most gnome hackers are probably accustomed to the fact that they can pass a null pointer as a value to glibc’s “%s” conversion character and get the string “(null)” output instead of a crash.

    I take offense at that assertion!

    Just kidding. Seriously though, this is definitely not something one should be accustomed to do. Not everyone is using GNU libc.

  6. 1052
    Posted October 1, 2007 at 8:56 am | Permalink

    It seems obvious to me that glibc should handle puts() in the same way it handles printf(), and it should do the same with any other function that may print a null string. It is called consistency. So open a bug report there http://sources.redhat.com/bugzilla/enter_bug.cgi

  7. blah
    Posted October 1, 2007 at 12:09 pm | Permalink

    Using a construct like printf(“%s”, NULL) in Gtk+ programs should be considered a bug anyway, unfortunately. On Solaris and other Unixes this construct will always cause a segfault.

    I think GLib should provide something macroish like this:

    static inline char *G_NONULLSTR(const char *s) {
    return s ? s : “(null)”;
    }

    Then, people could just write:

    printf(“%s\n”, G_NONULLSTR(s));

    Which is relatively readable, safe and even type-safe.

    On the Solaris printf() issue, see this:

    http://mail.opensolaris.org/pipermail/opensolaris-code/2006-July/thread.html#2972

    Unfortunately the Solaris people refused to copy glibc on this issue and kept their segfault-on-null printf implementation.

  8. Posted October 1, 2007 at 12:19 pm | Permalink

    This “feature” has caused me headaches when cross-compiling/running programs on windows, there it will just crash.

  9. Havoc
    Posted October 1, 2007 at 2:26 pm | Permalink

    This glibc feature is nothing but annoying, since it’s unportable, all it does is keep you from noticing the bug until someone ports to another libc…

  10. Posted October 1, 2007 at 3:46 pm | Permalink

    i’m not suggesting that i’d ever use “%s”, NULL in code intended to be portable (or even in production code). it’s just handy for debugging output and the like when you toss in a quick printf() to see what’s going on.

  11. David Schleef
    Posted October 1, 2007 at 7:32 pm | Permalink

    This feature certainly would suck if I wanted to override printf() at some time. Maybe, say, when I was using gdb.