8 months ago, I started to contribute to fortify-headers,
a standalone fortify-source implementation,
with the goal of implementing FORTIFY_SOURCE=3, since the current version
only implemented FORTIFY_SOURCE=2. I reached out to
sin, the original maintainer, to ask if he was
interested in my changes, and he told me the project wasn't maintained
anymore. But he would be happy to give me the commit bit instead. I spent
some months writing code before
accepting, to see if it would be a good idea: Would I be able to maintain it?
To improve it? Add more features? and so on. Turns out the answer is yes, and
I'm thus happy to announce the immediate availability of fortify-headers
2.0!
Changelog
- Added clang support, based on q66's patches.
- Fixed a 64b-related incompatibility around
ppoll - Added a ton of tests, with around 90% of coverage
- Made use of
__builtin_dynamic_object_sizewhenFORTIFY_SOURCE=3is used, instead of__builtin_object_size. - Made use of attributes: alloc_size, diagnose_as_builtin, diagnose_if, format, malloc, warn_unused_result, …
- Added some missing functions, like
calloc,fdopen,fmemopen,fprintf,malloc,memchr,popen,printf,qsort,umask, … - Added continuous integration, both on clang and gcc, covering the whole range of supported versions across the latest Ubuntu LTS.
Implementation details
Since this is a pretty uncommon piece of software, friends of mine have been
asking me details about the involved black magic.
While it's possible to overload functions with the
overloadable
attribute in C, there isn't really something similar for drive-by overloading.
Fortunately, it's possible to hack an equivalent by combining
#include_next with
the following macros:
#define _FORTIFY_STR(s) #s
#define _FORTIFY_ORIG(p, fn) __typeof__(fn) __orig_##fn __asm__(_FORTIFY_STR(p) #fn)
#define _FORTIFY_FNB(fn) _FORTIFY_ORIG(__USER_LABEL_PREFIX__, fn)
#define _FORTIFY_FN(fn) _FORTIFY_FNB(fn); _FORTIFY_INLINE
This makes the original function available when prefixed with __orig,
while allowing overloading.
On clang, the pass_object_size/pass_dynamic_object_size
attribute is used to pass down arguments size; the assembly label preventing
weird mangling issues. Since
it's only a label, despite being assembly, it's still portable across various
architectures. The _FORTIFY_INLINE macro contains all possible "please inline this
function" directives as possible, to avoid polluting the symbols.
There is of course a ton of #ifdef/#if __has_atribute/… to work around various
compiler intrinsics, like clang missing __builtin_va_arg_pack or gcc missing
diagnose_if, so that fortify-headers will always make use of the most
features available.
It is indeed a particularly gross pile of hacks, but this is C, also known as "nice things and why we can't have them."
Thanks to sin for creating the project and
maintaining it for years, strcat for his inspiring
work on fortifying bionic,
q66 for his clang patches and general support,
the friendly people from 2f30 for their patience,
Serge Sans Paille for his testsuite,
kevans for his work on fortifying
FreeBSD's libc,
Red Hat from pushing FORTIFY_SOURCE=2 and FORTIFY_SOURCE=3 forward,
...