检查的函数:
memcpy, mempcpy, memmove, memset, strcpy, stpcpy, strncpy, strcat, strncat, sprintf, vsprintf, snprintf, vsnprintf, gets
目的是检查dest变量内存是否溢出
Below are four different cases that can happen:
char buf[5];
/* 1) Known correct.
No runtime checking is needed, memcpy/strcpy
functions are called (or their equivalents inline). /
memcpy (buf, foo, 5);
strcpy (buf, “abcd”);
/ 2) Not known if correct, but checkable at runtime.
The compiler knows the number of bytes remaining in object,
but doesn’t know the length of the actual copy that will happen.
Alternative functions __memcpy_chk or __strcpy_chk are used in
this case that check whether buffer overflow happened. If buffer
overflow is detected, __chk_fail () is called (the normal action
is to abort () the application, perhaps by writing some message
to stderr. /
memcpy (buf, foo, n);
strcpy (buf, bar);
/ 3) Known incorrect.
The compiler can detect buffer overflows at compile
time. It issues warnings and calls the checking alternatives
at runtime. /
memcpy (buf, foo, 6);
strcpy (buf, “abcde”);
/ 4) Not known if correct, not checkable at runtime.
The compiler doesn’t know the buffer size, no checking
is done. Overflows will go undetected in these cases. */
memcpy (p, q, n);
strcpy (p, q);
In the current implementation mem{cpy,pcpy,move,set},
st{r,p,nc}py, str{,n}cat, {,v}s{,n}printf and gets functions
are checked this way.
如果编译器不知道dest变量的内存大小,则属于第四中情况
The diffence between -D_FORTIFY_SOURCE=1 and -D_FORTIFY_SOURCE=2
is e.g. for
struct S { struct T { char buf[5]; int x; } t; char buf[20]; } var;
With -D_FORTIFY_SOURCE=1,
strcpy (&var.t.buf[1], “abcdefg”);
is not considered an overflow (object is whole VAR), while
with -D_FORTIFY_SOURCE=2
strcpy (&var.t.buf[1], “abcdefg”);
will be considered a buffer overflow.
Another difference is that with -D_FORTIFY_SOURCE=2, %n
in format strings of the most common *printf family functions
is allowed only if it is stored in read-only memory (usually
string literals, gettext’s _("%s string %n") is fine too), but
usually when an attacker attempts to exploit a format string
vulnerability, %n will be somewhere where the attacker could
write it into.
直接看源码:
//bits/string3.h
#ifndef _STRING_H
# error "Never use directly; include instead."
#endif
# undef memcpy
__fortify_function void *
__NTH (memcpy (void *__restrict __dest, const void *__restrict __src,
size_t __len))
{
return __builtin___memcpy_chk (__dest, __src, __len, __bos0 (__dest));
}
//sys/cdefs.h
# define __fortify_function __extern_always_inline __attribute_artificial__
# define __NTH(fct) __attribute__ ((__nothrow__ __LEAF)) fct
/* Fortify support. */
#define __bos(ptr) __builtin_object_size (ptr, __USE_FORTIFY_LEVEL > 1)
#define __bos0(ptr) __builtin_object_size (ptr, 0)
//string.h
# if __USE_FORTIFY_LEVEL > 0 && defined __fortify_function
/* Functions with security checks. */
# include
# endif
如果-D_FORTIFY_SOURCE=2编译选项被指定,则__USE_FORTIFY_LEVEL 被定义并且指定为相应的值,
此时bits/string3.h被包括进来,undef memcpy后重新定义memcpy
如果要支持D_FORTIFY_SOURCE,glibc需要打上patch,加上__builtin___memcpy_chk 等的定义,同时gcc也需要打上patch,支持__USE_FORTIFY_LEVEL ,同时增加__builtin_object_size()内置函数的定义,这个的定义就是根据指针ptr得到对应变量的内存大小,感兴趣的可以去看gcc中的patch。
参考资料:
http://gcc.gnu.org/ml/gcc-patches/2004-09/msg02055.html
https://zatoichi-engineer.github.io/2017/10/06/fortify-source.html
https://idea.popcount.org/2013-08-15-fortify_source/
https://access.redhat.com/blogs/766093/posts/1976213