Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passsing null byte via format specifier in `printf`

Why does printf print a space instead of stopping when I use the NULL character from the ASCII table? This is what I mean:

printf("Hello%c, world", 0); //Hello , world
printf("Hello%c, world", '\0'); //Hello , world

Only when I put the escape character in the string itself printf stops the string:

printf("Hello\0, world"); //Hello

I tried this on Windows 8, Windows 10 (using cygwin, MinGW, Netbeans, Code::Blocks), XUbuntu, it's all the same.

Where is the problem? I asked one of my friends, but he said that he has no such problem, that all three examples executed the same way.

like image 530
sofia.bul Avatar asked Oct 22 '15 10:10

sofia.bul


3 Answers

printf("Hello\0, world"); uses its parameter as a C-string so it decodes it until it finds a NUL char, so it stops just after \0, ignoring what follows.

printf("Hello%c, world", 0); decodes its parameter (until it finds inside it a NUL char - i.e. after d), in the meanwhile it finds a %c, so it replaces it with the char given as parameter (whose ASCII code is NUL) and then send to the terminal a NUL char, and then continues.

Printf manual says:

These functions write the output under the control of a format string that specifies how subsequent arguments [...] are converted for output.

like image 51
Jean-Baptiste Yunès Avatar answered Nov 05 '22 09:11

Jean-Baptiste Yunès


You are taking a dependency on a printf() implementation detail. The low-level terminal output function requires the length of the string as an argument. There are two ways for printf() to do this.

The somewhat obvious way is to first format the string, then use strlen(). That's the one you hoped for.

But that's inefficient because it requires a double pass across the string buffer and appending 0. The other way to do it is track the formatted string length while substituting the fields, simply incrementing it for every appended character. Since it continues past the %c, you'll now get the larger length that includes everything past %c. What the terminal function does with the embedded 0 is an implementation detail as well, given that it is not a printable character. Seeing it substituted with a space is not uncommon.

Sane way to go about this is to not rely on implementation details.

like image 43
Hans Passant Avatar answered Nov 05 '22 08:11

Hans Passant


printf("Hello%c, world", 0); //Hello , world
printf("Hello%c, world", '\0'); //Hello , world

In both of these cases, you're trying to print out the character value corresponding to character code 0, which is not a printable character. I haven't found chapter and verse on it, but I suspect the behavior of trying to print a nul character value is unspecified or maybe even undefined. Either way, I would not expect it to be treated as a string terminator in this case.

printf("Hello\0, world"); //Hello

In this case, the nul character is part of the string constant and is interpreted by the compiler as a string terminator.

like image 1
John Bode Avatar answered Nov 05 '22 10:11

John Bode