Now you C me, now you don't
securitylab.github.com
securitylab.github.com
It might be an odd thing to have a strong opinion about, but I consider it poor form to use printf in a C hello world, for this reason. Stick with puts unless you need to use format-specifiers in the string.
In the interests of defensive programming, it makes sense to stick with the simpler and safer function unless there's a good reason to use the more complex and more dangerous function. printf should be used only when necessary. In a hello world, it's not necessary.
> Using a literal is perfectly safe and the intended usage.
It's not strictly true that it's perfectly safe. The literal could accidentally contain %n which would cause undefined behaviour. A little contrived, but consider Something has gone really #$@&%n£ wrong!
It's roughly the intended usage of printf, but it's precisely the intended usage of puts / fputs. Using them closes the door completely on format-specifier bugs.
For a simple example, how will puts print a string without a line termination?
Not completely joking here, I think everytime I've had to use strings to make two C programs communicate over a network, write() was as easy to use as printf(). Obviously it's not true when communicating with a user.
I am glad to hear you mention network coding. More people should do that, so fewer people make statements like, "always use puts for writing strings, never printf."
You aren't qualified to comment on my experience.
> For a simple example, how will puts print a string without a line termination?
Use fputs. If we're still just writing a simple string, there's no need to reach for a function with format-specifier interpretation.
I think the language barrier made you believe I was personally attacking you. You literally constructed a general argument from a specific statement. That is a logical fallacy called fallacy of composition.
If you find yourself printing a string with printf and getting a different result from fputs, it has nothing to do with a newline but with not having checked the string's contents. Ignoring that doesn't fix code with unvalidated input.
In C++ you can have iostream throw on error, avoiding the problem of missing manual checks.
printf %n is known to be dangerous for ages, and the secure variant printf_s which forbids it is nowhere implemented because politics. Almost nobody uses the -Wformat attributes in its declarations, and the wide variants of __attribute__(format(wprintf)) and wscanf are waiting to be implemented since 2008. Patches do exist for ages.
printf("...%n...",...) is fine[0], it's printf(not_a_literal,...) that's a problem, and forbidding %n just papers over it.
> Almost nobody uses the -Wformat attributes in its declarations
This is completely at odds with my experience (at least for any project serious enough to actually use -Wstuff options), although I don't claim that's necessarily representative.
> the wide variants of __attribute__(format(wprintf)) and wscanf are waiting to be implemented since 2008
So? Wide characters are evil and need to die; good riddance; use a real character encoding. This has nothing to do with printf, which operates on chars.
0: or least as fine as anything else that takes a pointer to a local variable to store results into; I guess you could argue that it ought to return everything as a struct/tuple, but C doesn't support variable-width structs.
$ cat tp.c
#include <stdio.h>
#include <string.h>
int main() {
char a[3];
printf(strcpy(a, "%n"));
return 0;
}
$ gcc -O3 tp.c ; ./a.out
*** %n in writable segment detected ***
Aborted (core dumped)https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/lin...
So no, I don't believe it will prevent using "%n" in memory that you have made writable. But this isn't really an issue because typically a format string exploit would have no way to arbitrarily call mprotect.
That said, there are other ways to abuse format string bugs apart from %n, for example "Direct Parameter Access" to get stack cookies, and the like:
%n: Print nothing, but writes the number of characters successfully written so far into an integer pointer parameter.
I like that because it describes me perfectly. Saving this.
I’m curious. What’s the reason behind not implementing a safer version of a function?
Take printf_s for instance, its main job is to forbid %n, at runtime[1]. It's much safer and more reliable to perform static analysis to mandate literal format strings and forbid %n.
[0] aka not very well designed, and not really good at leading developers away from mistakes, they also deviated from some "unsafe" functions in ways which could be quite critical e.g. strncpy_s infamously does not zero the destination buffer like strncpy, which might not be expected by people catering to codebases knowingly taking advantage of this property
[1] it will also check for %s null pointer which is somewhat more helpful, but again we're talking about C people here
Haven't seen a safe memset yet, other than mine.
So... M$FT bad, therefore anything they do is bad? That makes no sense.
That's backward; it's actually "anything [rather, most things] Microsoft does is bad, therefore Microsoft is bad".
And (most of) Annex K is a example of that.
(The strncpy/_s one is bit odd though, because strncpy was specifically for initializing a fixed-length buffer, which is not nul-terminated, but rather nul-padded. strncpy works fine for that purpose, although nobody uses nul-padded buffers anymore. Using it as a size-aware strcpy was always wrong; you should use strlen+memcpy or a separate str[lzc...]cpy (or strcpy_s I suppose) instead.)
So instead of fixing the security issues, like having some struct based type, the whole annex was made optional, thus everyone that was against it, just doesn't offer any alternative.
Instead OS vendors seem to go via hardware memory tagging solutions, than keeping up a lost war, as we have discussed multiple times.
Glibc will now print something like "(null)" in this case instead of segfaulting.
Safe and C strings do not belong into the same sentence unless it also includes words like: hazard, dumpster fire or rofl.
The more the better, after all security researchers, hackers and security tool vendors have families to feed.
I'm pretty sure josefx agrees with that given their second paragraph, they're mostly saying Annex K doesn't really make things better: it follows the usual (bad) C handling practices, and largely just adds more parameters to mess passing in.
Also, why can’t C have safe stuff? Because it’s always been possible to shoot yourself in the foot, we shouldn’t try to change that?
Maintainer time mostly and for some uses bloat.
> If you dynamically link, it makes no difference
I had projects that were limited by the time it took to load and initialize the program binary and loading/linking the C standard library was the largest dependency it had.
> and if you statically link, any decent linker won’t bring in what you don’t use
Santa, a sufficiently smart compiler and a decent linker walk into a bar. Here the joke segfaults because someone passed the wrong buffer size into sprintf_s.
That surprises me. Everywhere I've worked has used -Wallthethings or the equivalent, back to when it was `lint`.
At least WG21 is striving to reduce the known cases of UB and providing the necessary tooling to security conscious developers to make use of.
Citation? The last I heard the standard was fully defending compilers that actively seek out undefined behaviour as a excuse to inject bugs and vulnerabilities into working code under the pretense of optimization. It would be very good news if they were walking that back towards "undefined behaviour is solely for cross-platform compatiblity".