UNDER CONSTRUCTION
glibc 2.3.4 introduced _FORTIFY_SOURCE
in 2004 to catch misuse of some C library functions: buffer overflow and dangerous printf %n
uses. The initially supported functions was fprintf, gets, memcpy, memmove, mempcpy, memset, printf, snprintf, sprintf, stpcpy, strcat, strcpy, strncat, strncpy, vfprintf, vprintf, vsnprintf, vsprintf
. See:
- GCC [PATCH] Object size checking to prevent (some) buffer overflows
- glibc commit b5cc329c4fb831ce99cd683caf3c5b5114c90010
More functions were added over time.
Let's walk through a simple C program to see how fortify source works with a modern glibc.
1 | #include <stdio.h> |
Compile this program with -D_FORTIFY_SOURCE=2
together with optimization level -O1
or above.
features.h
checks fortify level and optimization level, and defines __USE_FORTIFY_LEVEL
.
1 | #if defined _FORTIFY_SOURCE && _FORTIFY_SOURCE > 0 |
string.h
provides a declaration of strcpy
and includes bits/string_fortified.h
.
1 | extern char *strcpy (char *__restrict __dest, const char *__restrict __src) |
bits/string_fortified.h
defines
1 | __fortify_function char * |
__fortify_function
expands to extern __inline __attribute__ ((__always_inline__)) __attribute__ ((__gnu_inline__)) __attribute__ ((__artificial__))
. This is an extern inline
GNU inline function which is only used for inlining and is not compiled on its own (no definition in the relocatable object file). gnu_inline
is also available in C++ mode and provides the desired behavior. (In LLVM, the function has the available_externally
linkage, instead of having the linkonce_odr
linkage in a COMDAT.) (Since there is an external definition in libc, the extern inline
GNU inline function is like an inline
C99 inline function.)
always_inline
tells the compiler to always inline a direct call to the function. This is important for __builtin_object_size
in the callee to know the argument object.
When debug information is produced, artificial
marks the function as an artificial entity (DW_AT_artificial
):
Any debugging information entry representing the declaration of an object or type artificially generated by a compiler and not explicitly declared by the source program may have a DW_AT_artificial attribute, which is a flag.
__glibc_objsize
uses a compiler built-in function to determine the object size.
1 |
|
With _FORTIFY_SOURCE==2
, strcpy(a, argv[1]);
expands to __builtin___strcpy_chk(a, argv[1], __builtin_object_size(a, 1));
. __builtin___strcpy_chk
lowers to either a strcpy
or __strcpy_chk
function call, depending whether the object size can be determined.
__strcpy_chk
is used if the destination size can be determined to be a constant. __strcpy_chk
is defined in debug/strcpy_chk.c
:
1 |
|
In the strcpy(a, argv[1]);
example, since a
's size is known, the statement is like __strcpy_chk(a, argv[1], 4);
.
strcpy
is used if the destination size cannot be determined to be a constant.
1 | void foo(char *b) { strcpy(b, a); } |
In the GCC repository, run gre _chk gcc/builtins.def
to get the list of builtin functions for fortify source.
_FORTIFY_SOURCE=1
vs _FORTIFY_SOURCE=2
1 | struct A { |
_FORTIFY_SOURCE=1
:__strcpy_chk(&g.b.a[1], b, 11)
_FORTIFY_SOURCE=2
:__strcpy_chk(&g.b.a[1], b, 3)
See the GCC documentation about the difference: Object Size Checking Built-in Functions.
_FORTIFY_SOURCE=3
Clang 8 implemented a new builtin function __builtin_dynamic_object_size
. glibc added support on 2021-12-31 and early 2022. See Introduce _FORTIFY_SOURCE=3 and follow-ups.
GCC ported the feature in 2021-12 and glibc added support for GCC 12 on 2022-01-12. Interestingly, this is a glibc feature that Clang support happened before GCC.
1 | void *foo(size_t a, size_t b, const void *src, size_t n) { |
The memcpy
call lowers to __memcpy_chk(buf, src, n, a * b)
where a * b
is not a constant.
-Wstringop-overflow
-Wfortify-source
Some buffer overflow issues can be determined at compile time (both the source and destination sizes are known). GCC provides a warning -Wstringop-overflow
. Clang -Wfortify-source
is similar.
1 | char a[4]; |
glibc has annotated many functions with the access
attribute (see BZ #25219). These attributes help GCC know the accessed number of elements for pointer parameters and catch bugs at compile time.
__builtin_va_arg_pack, __builtin_va_arg_pack_len
When the two builtin functions are available, __va_arg_pack
is defined and some functions like printf
are fortified as well. As Clang's documentation indicates, the two builtin functions are not implemented.
1 | __fortify_function int |
With _FORTIFY_SOURCE >= 2
, %n
causes *** %n in writable segment detected ***
with a if __fmt
is in a writable segment. On Linux, /proc/self/maps
is scanned (sysdeps/unix/sysv/linux/readonly-area.c
).
Clang support
Clang supports many of the builtin string functions. On the other hand, it tries to provide a more principled set of language extensions to make a better fortify source implementation. See pass_object_size, pass_dynamic_object_size. The two attributes can be used together with overloadable
and diagnose_if
. See The Anatomy of Clang FORTIFY.
Flexible array member
Before C99 introduced flexible array member, many projects had used a proto-flexible-array-member feature by abusing the last array member of a structure. GCC and Clang have implemented workarounds for such code. See __builtin_object_size(P->M, 1) where M is an array and the last member of a struct fails how the workarounds make __builtin_object_size
prone to return -1 and defeat _FORTIFY_SOURCE
for some code.
Alternative implementations
https://git.2f30.org/fortify-headers/
ChromeOS patches glibc to use Clang-style _FORTIFY_SOURCE
which is based on the pass_object_info
attribute. See https://crbug.com/638456.
Android bionic added GCC-style _FORTIFY_SOURCE
in 2012-06. It gained Clang-style _FORTIFY_SOURCE
support in 2017-02 and removed GCC-style _FORTIFY_SOURCE
support in 2018-07.
Linux kernel introduced CONFIG_FORTIFY_SOURCE
in 2017-07.
_FORTIFY_SOURCE
vs sanitizers
In terms of bug catching capability, _FORTIFY_SOURCE
does not perform as well as some dynamic instrumentation tools. There are many cases it cannot handle. The buffer overflow checks can be detected by AddressSanitizer and HWAddressSanitizer as well. Integer multiplication overflow (e.g. __fread_chk
) can be detected by UndefinedSanitizer. _FORTIFY_SOURCE
's streghth lies in its deployment convenience and very small overhead (code size, performance, memory usage).
I think it will be interesting if someone can compare _FORTIFY_SOURCE
with some mature static analyzers.