Why is printf with a single argument (without conversion specifiers) deprecated?
In a book that I'm reading, it's written that printf
with a single argument (without conversion specifiers) is deprecated. It recommends to substitute
printf("Hello World!");
with
puts("Hello World!");
or
printf("%s", "Hello World!");
Can someone tell me why printf("Hello World!");
is wrong? It is written in the book that it contains vulnerabilities. What are these vulnerabilities?
printf("Hello World!");
is IMHO not vulnerable but consider this:
const char *str;
...
printf(str);
If str
happens to point to a string containing %s
format specifiers, your program will exhibit undefined behaviour (mostly a crash), whereas puts(str)
will just display the string as is.
Example:
printf("%s"); //undefined behaviour (mostly crash)
puts("%s"); // displays "%s\n"
printf("Hello world");
is fine and has no security vulnerability.
The problem lies with:
printf(p);
where p
is a pointer to an input that is controlled by the user. It is prone to format strings attacks: user can insert conversion specifications to take control of the program, e.g., %x
to dump memory or %n
to overwrite memory.
Note that puts("Hello world")
is not equivalent in behavior to printf("Hello world")
but to printf("Hello world\n")
. Compilers usually are smart enough to optimize the latter call to replace it with puts
.
Further to the other answers, printf("Hello world! I am 50% happy today")
is an easy bug to make, potentially causing all manner of nasty memory problems (it's UB!).
It's just simpler, easier and more robust to "require" programmers to be absolutely clear when they want a verbatim string and nothing else.
And that's what printf("%s", "Hello world! I am 50% happy today")
gets you. It's entirely foolproof.
(Steve, of course printf("He has %d cherries\n", ncherries)
is absolutely not the same thing; in this case, the programmer is not in "verbatim string" mindset; she is in "format string" mindset.)
I'll just add a bit of information regarding the vulnerability part here.
It's said to be vulnerable because of printf string format vulnerability. In your example, where the string is hardcoded, it's harmless (even if hardcoding strings like this is never fully recommended). But specifying the parameter's types is a good habit to take. Take this example:
If someone puts format string character in your printf instead of a regular string (say, if you want to print the program stdin), printf will take whatever he can on the stack.
It was (and still is) very used to exploit programs into exploring stacks to access hidden information or bypass authentication for example.
Example (C):
int main(int argc, char *argv[])
{
printf(argv[argc - 1]); // takes the first argument if it exists
}
if I put as input of this program "%08x %08x %08x %08x %08x\n"
printf ("%08x %08x %08x %08x %08x\n");
This instructs the printf-function to retrieve five parameters from the stack and display them as 8-digit padded hexadecimal numbers. So a possible output may look like:
40012980 080628c4 bffff7a4 00000005 08059c04
See this for a more complete explanation and other examples.
Calling printf
with literal format strings is safe and efficient, and there
exist tools to automatically warn you if your invocation of printf
with user
provided format strings is unsafe.
The most severe attacks on printf
take advantage of the %n
format
specifier. In contrast to all other format specifiers, e.g. %d
, %n
actually
writes a value to a memory address provided in one of the format arguments.
This means that an attacker can overwrite memory and thus potentially take
control of your program. Wikipedia
provides more detail.
If you call printf
with a literal format string, an attacker cannot sneak
a %n
into your format string, and you are thus safe. In fact,
gcc will change your call to printf
into a call to puts
, so there litteraly
isn't any difference (test this by running gcc -O3 -S
).
If you call printf
with a user provided format string, an attacker can
potentially sneak a %n
into your format string, and take control of your
program. Your compiler will usually warn you that his is unsafe, see
-Wformat-security
. There are also more advanced tools that ensure that
an invocation of printf
is safe even with user provided format strings, and
they might even check that you pass the right number and type of arguments to
printf
. For example, for Java there is Google's Error Prone
and the Checker Framework.