While writing about the -O0
optimisation level I found some interesting problems related to the fortified source support in recent GCC and GLIBC releases, which we enable by default since GCC 4.3.3-r1 and that is used as a partial replacement of stack-smashing (which hopefully, thanks to Magnus, will soon come back into Gentoo!).
As I said in that post, the special fortified functions (those that are available in the form of __$func_chk
in the libc.so
file and provide warnings at build time, and proper stack traces at runtime) only get enabled if inline functions are enabled, so are totally ignored at -O0
(simply disabling inlines, by using -fno-inline
won’t stop them from being used, though). This means that if we have some software that does not respect the CFLAGS
variables, and uses -O0
in whatever context, it’s currently not using fortified sources, and as such, it can crash without warning about it beforehand.
But lack of optimisation – which is a quite rare occurrence, luckily – is not the only thing that may cause the fortified versions of functions to not be emitted. Since the fortified versions of the functions are declared (and the wrappers defined) in files such as /usr/include/bits/stdio2.h
, you need to include them properly for them to work. Unfortunately, it’s way too common for projects not to take headers seriously and leave functions to be implicitly defined. But the implicit-definition does not take care of the fortified sources.
On the other hand, during testing of these little differences, I found some things that aren’t obvious at all. For instance, when you do use fortified sources, GCC fails to optimise some slightly more sophisticated cases. For instance this is the code I used to test implicit declarations:
#ifndef IMPLICIT
# include <stdio.h>
#endif
int main() {
char foo[12];
return sprintf(foo, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}
Code language: PHP (php)
When built with -DIMPLICIT -O1
(so causing sprintf()
to be implicitly defined), the produced executable does not crash, while it does crash with -O0
(see why I say -O0
produces different code?). The reason for that is that GCC picks up the call to sprintf()
as a built-in function, whose semantic is known to it. In general, GCC cannot drop function calls that even just might have side effects, but in this case, GCC knows what the side-effect is (writing to the foo
variable); its parameters are also known, so it can replace the function with its effects straight during the build phase. But since the foo
variable is never read from, the whole copy is a moot effect. The only thing that we care about the sprintf
call is the return value, which represent the length of the content written to the string, and is also constant since the compiler knows would be 26.
Indeed, the emitted code for that function, compiled that way is as such:
main:
movl $26, %eax
ret
Interesting, isn’t it? Now this has two related side effects: the first is that the fortified function is not used, so there is no warning printed at build-time, the other is that the code will not crash at runtime because the buffer overflow is gone altogether! Now, can this be considered a bug in GCC? I don’t think so, it’s the code that is wrong to begin with. But you can see how disabling optimisations has now introduced a crash site. You can see how the code would otherwise work by adding -fno-builtin
to the compiler. This will remove the semantic knowledge of the call from the compiler optimisers; this results in a straight call to sprintf()
.
But this post need to have at least a bottom line to be worth anything, so here there are some:
- Gentoo treasures stability and security, for this reason why enable fortified sources by default; if what you desire is pure speed, without caring about safety, you may
-D_FORTIFY_SOURCE=0
to yourCFLAGS
variable; this will override the fortified sources as specified by the Gentoo spec file; if you do so, though, please do not file bugs unless you provide a patch with them as well; - even with the tinderbox at hand, I have near to no way to find out whether the fortified functions are used or not; as far as I can tell, the fortified version is not emitted when the important parameters are not build-time constant (obviously);
- one way to at least reduce the impact of possibly skipped fortified checks is to get rid of implicit declarations; Portage already takes care of reporting them as QA warnings at the end of the merge; unfortunately, most maintainers won’t appreciate patches to remove implicit declarations because they are usually boring, and require to be changed from release to release, if not properly sent upstream; if you care about security and safety, please do take care of those warnings; if you’re not the maintainer, send them upstream, asking them nicely to add them to the next release;
- even if we don’t have warnings about implicit declarations, there is the risk that some stupid software decides to declare the sysem functions manually rather than relying on those provided by the C library; this usually happens when the software is trying to be portable but wants to feel smarter than average; goes without saying that it becomes a mess to identify that software and properly fix it up.
Nice post!On a related subject, any reason why gcc happily accepts:<typo:code>gcc -DIMPLICIT -std=c99 ccc.cccc.c: In function ‘main’:ccc.c:7: warning: implicit declaration of function ‘sprintf’ccc.c:7: warning: incompatible implicit declaration of built-in function ‘sprintf’gcc -DIMPLICIT -fno-builtin -std=c99 ccc.cccc.c: In function ‘main’:ccc.c:7: warning: implicit declaration of function ‘sprintf'</typo:code>While clang does it a bit different:<typo:code>clang -DIMPLICIT -std=c99 ccc.cccc.c:7:16: warning: implicitly declaring C library function ‘sprintf’ with type ‘int (char *, char const *, …)’ [-pedantic] return sprintf(foo, “ABCDEFGHIJKLMNOPQRSTUVWXYZ”);ccc.c:7:16: note: please include the header <stdio.h> or explicitly provide a declaration for ‘sprintf’clang -DIMPLICIT -fno-builtin -std=c99 ccc.cccc.c:7:16: warning: implicit declaration of function ‘sprintf’ is invalid in C99 [-Wimplicit-function-declaration] return sprintf(foo, “ABCDEFGHIJKLMNOPQRSTUVWXYZ”);</typo:code>Just pointing out difference in choices made between C and C++ 🙂
As far as I know, it _should_ be an error in C99; neither seem to consider it so though, mostly because of a number of legacy software still using the old syntax, starting from autoconf.Indeed, autoconf’s @AC_CHECK_FUNCS@ checks tend to use implicit declarations, so they wouldn’t work correctly with stuff like @-Werror-implicit-declaration@ as those make it really invalid.
There’s also this bug: http://bugs.winehq.org/show…, that was “solved” by undefining _FORTIFY_SOURCE.I still wonder if the problem couldn’t be solved without it.