fortify-headers 2.0
Tue 12 December 2023 — download

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_size when FORTIFY_SOURCE=3 is 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, ...