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 thoughts on “gcc feature breaks glibc feature”

  1. 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…

  2. 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.

  3. 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.

  4. 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.

  5. 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…

  6. 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.

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

Comments are closed.