Defining this macro causes some lightweight checks to be performed to detect some buffer overflow errors when employing various string and memory manipulation functions.
for example, memcpy(3), memset(3), stpcpy(3), strcpy(3), strncpy(3), strcat(3), strncat(3), sprintf(3), snprintf(3), vsprintf(3), vsnprintf(3), gets(3), and ... variants thereof.
returns a constant number of bytes from ptr to the end of the object ptr pointer points to (if known at compile time)
Depending on type returns the minimum or maximum of the object(s) ptr might point to, the closest surrounding subobject or the outer object and either 0 or -1/SIZE_MAX.
https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html
to detect object sizes across function boundaries or to follow pointer assignments through non-trivial control flow they rely on various optimization passes enabled with -O2
glibc include files are almost unreadable, so we'll pretend we are only interested in -D_FORTIFY_SOURCE=2 with the "simplest" semantics.
returns SIZE_MAX if unknown, otherwise largest remaining size sub-object ptr points to.
struct V { char buf1[10]; int b; char buf2[10]; } var;
char *p = &var.b;
char *q = &var.buf1[1];
char *r = x > 0 ? p : q;
#define bos(ptr) __builtin_object_size (ptr, 1)
bos (p) == sizeof (var.b); // 4
bos (q) == sizeof (var.buf1) - 1; // 9
bos (r) == max (bos (p), bos (q)); // 9
See https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html for all the details. Try type = 0 => r == 27, type = 2 => r == 16, type = 3 => r == 4.
char *getcwd (char *buf, size_t size);
char *
__getcwd_chk (char *buf, size_t size, size_t buflen)
{
if (size > buflen)
__chk_fail ();
return __getcwd (buf, size);
}
extern char *__getcwd_chk (char *__buf, size_t __size, size_t __buflen);
extern char *__REDIRECT (__getcwd_alias, ( /* ... */ ), getcwd);
extern char *__REDIRECT (__getcwd_chk_warn, ( /* ... */ ), __getcwd_chk)
__warnattr ("getcwd caller with bigger length than size of "
"destination buffer");
__extern_always_inline __attribute_artificial__ char *
getcwd (char *__buf, size_t __size)
{
if (__bos (__buf) != (size_t) -1)
{
if (!__builtin_constant_p (__size))
return __getcwd_chk (__buf, __size, __bos (__buf));
if (__size > __bos (__buf))
return __getcwd_chk_warn (__buf, __size, __bos (__buf));
}
return __getcwd_alias (__buf, __size);
}
char *path = malloc (16);
char *cwd = getcwd (path, PATH_MAX);
printf ("cwd: %s\n", cwd);
$ ./getcwd
cwd: /home/mark
$ valgrind -q ./getcwd
==24741== Syscall param getcwd(buf) points to unaddressable byte(s)
==24741== at 0x4F324CA: getcwd (getcwd.c:78)
==24741== by 0x4004BA: main (getcwd.c:10)
==24741== Address 0x5200050 is 0 bytes after a block of size 16 alloc'd
==24741== at 0x4C2DB9D: malloc (vg_replace_malloc.c:299)
==24741== by 0x4004AD: main (getcwd.c:9)
$ gcc -D_FORTIFY_SOURCE=2 -O2 -g -o getcwd getcwd.c
In file included from /usr/include/unistd.h:1163:0,
from getcwd.c:2:
In function ‘getcwd’,
inlined from ‘main’ at getcwd.c:10:9:
/usr/include/bits/unistd.h:208:9: warning: call to ‘__getcwd_chk_warn’
declared with attribute warning: getcwd caller with bigger length than
size of destination buffer
return __getcwd_chk_warn (__buf, __size, __bos (__buf));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ ./getcwd
*** buffer overflow detected ***: ./getcwd terminated
======= Backtrace: =========
/lib64/libc.so.6(+0x791fb)[0x7fa990cc01fb]
/lib64/libc.so.6(__fortify_fail+0x37)[0x7fa990d61187]
/lib64/libc.so.6(+0x118120)[0x7fa990d5f120]
/lib64/libc.so.6(+0x1186c3)[0x7fa990d5f6c3]
./getcwd[0x400500]
/lib64/libc.so.6(__libc_start_main+0xf1)[0x7fa990c67401]
./getcwd[0x40054a]
======= Memory map: ========
00400000-00401000 r-xp 00000000 b3:04 1185378 /home/mark/getcwd
00600000-00601000 r--p 00000000 b3:04 1185378 /home/mark/getcwd
[...]
7ffd50dbc000-7ffd50ddd000 rw-p 00000000 00:00 0 [stack]
7ffd50df7000-7ffd50df9000 r--p 00000000 00:00 0 [vvar]
7ffd50df9000-7ffd50dfb000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 [vsyscall]
Aborted (core dumped)
$ valgrind -q ./getcwd
*** buffer overflow detected ***: ./getcwd terminated
======= Backtrace: =========
[...]
======= Memory map: ========
[...]
==29012== Process terminating with default action of signal 6 (SIGABRT)
==29012== at 0x4E6F91F: raise (raise.c:58)
==29012== by 0x4E71519: abort (abort.c:89)
==29012== by 0x4EB31FF: __libc_message (libc_fatal.c:175)
==29012== by 0x4F54186: __fortify_fail (fortify_fail.c:30)
==29012== by 0x4F5211F: __chk_fail (chk_fail.c:28)
==29012== by 0x4F526C2: __getcwd_chk (getcwd_chk.c:27)
==29012== by 0x4004FF: getcwd (unistd.h:208)
==29012== by 0x4004FF: main (getcwd.c:10)
char *
__getcwd_chk (char *buf, size_t size, size_t buflen)
{
if (size > buflen)
__chk_fail (buf + buflen);
return __getcwd (buf, size);
}
#include <valgrind/memcheck.h>
void
__attribute__ ((noreturn))
__chk_fail (char *ptr)
{
VALGRIND_MAKE_MEM_NOACCESS(ptr,1);
VALGRIND_CHECK_MEM_IS_ADDRESSABLE(ptr,1);
__fortify_fail ("buffer overflow detected");
}
libc_hidden_def (__chk_fail)
$ valgrind -q ./getcwd
==30124== Unaddressable byte(s) found during client check request
==30124== by 0x4F5211F: __chk_fail (chk_fail.c:30)
==30124== by 0x4F526C2: __getcwd_chk (getcwd_chk.c:27)
==30124== by 0x4004FF: getcwd (unistd.h:208)
==30124== by 0x4004FF: main (getcwd.c:10)
==30124== Address 0x5200050 is 0 bytes after a block of size 16 alloc'd
==30124== at 0x4C2DB9D: malloc (vg_replace_malloc.c:299)
==30124== by 0x4004FD: main (getcwd.c:30)
*** buffer overflow detected ***: ./getcwd terminated
======= Backtrace: =========
[...]
======= Memory map: ========
[...]
__builtin___memcpy_chk __builtin___memmove_chk __builtin___mempcpy_chk
__builtin___memset_chk __builtin___memmove_chk __builtin___memset_chk
__builtin___strcpy_chk __builtin___stpcpy_chk __builtin___strncpy_chk
__builtin___strcat_chk __builtin___strncat_chk
Table of Contents | t |
---|---|
Exposé | ESC |
Full screen slides | e |
Presenter View | p |
Source Files | s |
Slide Numbers | n |
Toggle screen blanking | b |
Show/hide slide context | c |
Notes | 2 |
Help | h |