gcc: Object Size Checking

1 
1 6.56 Object Size Checking Built-in Functions
1 ============================================
1 
1 GCC implements a limited buffer overflow protection mechanism that can
1 prevent some buffer overflow attacks by determining the sizes of objects
1 into which data is about to be written and preventing the writes when
1 the size isn't sufficient.  The built-in functions described below yield
1 the best results when used together and when optimization is enabled.
1 For example, to detect object sizes across function boundaries or to
1 follow pointer assignments through non-trivial control flow they rely on
1 various optimization passes enabled with '-O2'.  However, to a limited
1 extent, they can be used without optimization as well.
1 
1  -- Built-in Function: size_t __builtin_object_size (const void * PTR,
1           int TYPE)
1      is a built-in construct that returns a constant number of bytes
1      from PTR to the end of the object PTR pointer points to (if known
1      at compile time).  '__builtin_object_size' never evaluates its
1      arguments for side effects.  If there are any side effects in them,
1      it returns '(size_t) -1' for TYPE 0 or 1 and '(size_t) 0' for TYPE
1      2 or 3.  If there are multiple objects PTR can point to and all of
1      them are known at compile time, the returned number is the maximum
1      of remaining byte counts in those objects if TYPE & 2 is 0 and
1      minimum if nonzero.  If it is not possible to determine which
1      objects PTR points to at compile time, '__builtin_object_size'
1      should return '(size_t) -1' for TYPE 0 or 1 and '(size_t) 0' for
1      TYPE 2 or 3.
1 
1      TYPE is an integer constant from 0 to 3.  If the least significant
1      bit is clear, objects are whole variables, if it is set, a closest
1      surrounding subobject is considered the object a pointer points to.
1      The second bit determines if maximum or minimum of remaining bytes
1      is computed.
1 
1           struct V { char buf1[10]; int b; char buf2[10]; } var;
1           char *p = &var.buf1[1], *q = &var.b;
1 
1           /* Here the object p points to is var.  */
1           assert (__builtin_object_size (p, 0) == sizeof (var) - 1);
1           /* The subobject p points to is var.buf1.  */
1           assert (__builtin_object_size (p, 1) == sizeof (var.buf1) - 1);
1           /* The object q points to is var.  */
1           assert (__builtin_object_size (q, 0)
1                   == (char *) (&var + 1) - (char *) &var.b);
1           /* The subobject q points to is var.b.  */
1           assert (__builtin_object_size (q, 1) == sizeof (var.b));
1 
1  There are built-in functions added for many common string operation
1 functions, e.g., for 'memcpy' '__builtin___memcpy_chk' built-in is
1 provided.  This built-in has an additional last argument, which is the
1 number of bytes remaining in the object the DEST argument points to or
1 '(size_t) -1' if the size is not known.
1 
1  The built-in functions are optimized into the normal string functions
1 like 'memcpy' if the last argument is '(size_t) -1' or if it is known at
1 compile time that the destination object will not be overflowed.  If the
1 compiler can determine at compile time that the object will always be
1 overflowed, it issues a warning.
1 
1  The intended use can be e.g.
1 
1      #undef memcpy
1      #define bos0(dest) __builtin_object_size (dest, 0)
1      #define memcpy(dest, src, n) \
1        __builtin___memcpy_chk (dest, src, n, bos0 (dest))
1 
1      char *volatile p;
1      char buf[10];
1      /* It is unknown what object p points to, so this is optimized
1         into plain memcpy - no checking is possible.  */
1      memcpy (p, "abcde", n);
1      /* Destination is known and length too.  It is known at compile
1         time there will be no overflow.  */
1      memcpy (&buf[5], "abcde", 5);
1      /* Destination is known, but the length is not known at compile time.
1         This will result in __memcpy_chk call that can check for overflow
1         at run time.  */
1      memcpy (&buf[5], "abcde", n);
1      /* Destination is known and it is known at compile time there will
1         be overflow.  There will be a warning and __memcpy_chk call that
1         will abort the program at run time.  */
1      memcpy (&buf[6], "abcde", 5);
1 
1  Such built-in functions are provided for 'memcpy', 'mempcpy',
1 'memmove', 'memset', 'strcpy', 'stpcpy', 'strncpy', 'strcat' and
1 'strncat'.
1 
1  There are also checking built-in functions for formatted output
1 functions.
1      int __builtin___sprintf_chk (char *s, int flag, size_t os, const char *fmt, ...);
1      int __builtin___snprintf_chk (char *s, size_t maxlen, int flag, size_t os,
1                                    const char *fmt, ...);
1      int __builtin___vsprintf_chk (char *s, int flag, size_t os, const char *fmt,
1                                    va_list ap);
1      int __builtin___vsnprintf_chk (char *s, size_t maxlen, int flag, size_t os,
1                                     const char *fmt, va_list ap);
1 
1  The added FLAG argument is passed unchanged to '__sprintf_chk' etc.
1 functions and can contain implementation specific flags on what
1 additional security measures the checking function might take, such as
1 handling '%n' differently.
1 
1  The OS argument is the object size S points to, like in the other
1 built-in functions.  There is a small difference in the behavior though,
1 if OS is '(size_t) -1', the built-in functions are optimized into the
1 non-checking functions only if FLAG is 0, otherwise the checking
1 function is called with OS argument set to '(size_t) -1'.
1 
1  In addition to this, there are checking built-in functions
1 '__builtin___printf_chk', '__builtin___vprintf_chk',
1 '__builtin___fprintf_chk' and '__builtin___vfprintf_chk'.  These have
1 just one additional argument, FLAG, right before format string FMT.  If
1 the compiler is able to optimize them to 'fputc' etc. functions, it
1 does, otherwise the checking function is called and the FLAG argument
1 passed to it.
1