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:If
str
happens to point to a string containing%s
format specifiers, your program will exhibit undefined behaviour (mostly a crash), whereasputs(str)
will just display the string as is.Example:
printf("Hello world");
is fine and has no security vulnerability.
The problem lies with:
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 toprintf("Hello world")
but toprintf("Hello world\n")
. Compilers usually are smart enough to optimize the latter call to replace it withputs
.This is misguided advice. Yes, if you have a run-time string to print,
is quite dangerous, and you should always use
instead, because in general you can never know whether
str
might contain a%
sign. However, if you have a compile-time constant string, there's nothing whatsoever wrong with(Among other things, that is the most classic C program ever, literally from the C programming book of Genesis. So anyone deprecating that usage is being rather heretical, and I for one would be somewhat offended!)
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):
if I put as input of this program
"%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:
See this for a more complete explanation and other examples.
Since no one has mentioned, I'd add a note regarding their performance.
Under normal circumstances, assuming no compiler optimisations are used (i.e.
printf()
actually callsprintf()
and notfputs()
), I would expectprintf()
to perform less efficiently, especially for long strings. This is becauseprintf()
has to parse the string to check if there are any conversion specifiers.To confirm this, I have run some tests. The testing is performed on Ubuntu 14.04, with gcc 4.8.4. My machine uses an Intel i5 cpu. The program being tested is as follows:
Both are compiled with
gcc -Wall -O0
. Time is measured usingtime ./a.out > /dev/null
. The following is the result of a typical run (I've run them five times, all results are within 0.002 seconds).For the
printf()
variant:For the
fputs()
variant:This effect is amplified if you have a very long string.
For the
printf()
variant (ran three times, real plus/minus 1.5s):For the
fputs()
variant (ran three times, real plus/minus 0.2s):Note: After inspecting the assembly generated by gcc, I realised that gcc optimises the
fputs()
call to anfwrite()
call, even with-O0
. (Theprintf()
call remains unchanged.) I am not sure whether this will invalidate my test, as the compiler calculates the string length forfwrite()
at compile-time.For gcc it is possible to enable specific warnings for checking
printf()
andscanf()
.The gcc documentation states:
The
-Wformat
which is enabled within the-Wall
option does not enable several special warnings that help to find these cases:-Wformat-nonliteral
will warn if you do not pass a string litteral as format specifier.-Wformat-security
will warn if you pass a string that might contain a dangerous construct. It's a subset of-Wformat-nonliteral
.I have to admit that enabling
-Wformat-security
revealed several bugs we had in our codebase (logging module, error handling module, xml output module, all had some functions that could do undefined things if they had been called with % characters in their parameter. For info, our codebase is now around 20 years old and even if we were aware of these kind of problems, we were extremely surprised when we enabled these warnings how many of these bugs were still in the codebase).