bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] inttostr.h: add compile-time buffer overrun checks


From: Jim Meyering
Subject: Re: [PATCH] inttostr.h: add compile-time buffer overrun checks
Date: Sun, 17 Oct 2010 10:30:09 +0200

Bruno Haible wrote:

> Hi Jim,
>
>> +# define inttostr(n, s)                                                 \
>> +   ((void) verify_true (sizeof (s) == sizeof (void *)                   \
>> +                        || INT_BUFSIZE_BOUND (int) <= sizeof (s)),      \
>> +    (inttostr) (n, s))
>
> Nice and clever trick.
>
> Unfortunately, it does not work for variable-length arrays, which are allowed
> in C99 and C++.
>
> Test case:
>
>   void foo (int n)
>   {
>     char buf[10 + (n < 0) + 1];
>     char *result = inttostr (n, buf);
>   }

Good counterexample.

> Yields:
>
>   foo.c: In function ‘foo’:
>   foo.c:38: error: bit-field ‘verify_error_if_negative_size__’ width not an 
> integer constant

That fails because INT_BUFSIZE_BOUND(int) is 12 to accommodate
the negative sign on INT_MIN, yet sizeof(buf) is only 11.

> How to fix this? I tried __builtin_constant_p and __builtin_choose_expr, but
> haven't found the trick.

If there is no better work-around, I will add a comment advising such users
to use INT_BUFSIZE_BOUND(VAR) (hence no need for a variable-length array and
no need to change numbers if you change the type of VAR), or failing that,
to add an "#undef inttostr" in the affected compilation unit, thus disabling
the overrun check.

---------------------------------------------
While looking through gcc's documentation (latest upstream),
I noticed this, which can be put to good use in xalloc.h:

    @item alloc_size
    @cindex @code{alloc_size} attribute
    The @code{alloc_size} attribute is used to tell the compiler that the
    function return value points to memory, where the size is given by
    one or two of the functions parameters.  GCC uses this
    information to improve the correctness of @code{__builtin_object_size}.

    The function parameter(s) denoting the allocated size are specified by
    one or two integer arguments supplied to the attribute.  The allocated size
    is either the value of the single function argument specified or the product
    of the two function arguments specified.  Argument numbering starts at
    one.

    For instance,

    @smallexample
    void* my_calloc(size_t, size_t) __attribute__((alloc_size(1,2)))
    void my_realloc(void*, size_t) __attribute__((alloc_size(2)))
    @end smallexample

    declares that my_calloc will return memory of the size given by
    the product of parameter 1 and 2 and that my_realloc will return memory
    of the size given by parameter 2.

--------------------------------------
Continuing in the same vein, there's even more:

@smallexample
#undef memcpy
#define bos0(dest) __builtin_object_size (dest, 0)
#define memcpy(dest, src, n) \
  __builtin___memcpy_chk (dest, src, n, bos0 (dest))

char *volatile p;
char buf[10];
/* It is unknown what object p points to, so this is optimized
   into plain memcpy - no checking is possible.  */
memcpy (p, "abcde", n);
/* Destination is known and length too.  It is known at compile
   time there will be no overflow.  */
memcpy (&buf[5], "abcde", 5);
/* Destination is known, but the length is not known at compile time.
   This will result in __memcpy_chk call that can check for overflow
   at runtime.  */
memcpy (&buf[5], "abcde", n);
/* Destination is known and it is known at compile time there will
   be overflow.  There will be a warning and __memcpy_chk call that
   will abort the program at runtime.  */
memcpy (&buf[6], "abcde", 5);
@end smallexample

Such built-in functions are provided for @code{memcpy}, @code{mempcpy},
@code{memmove}, @code{memset}, @code{strcpy}, @code{stpcpy}, @code{strncpy},
@code{strcat} and @code{strncat}.

There are also checking built-in functions for formatted output functions.
@smallexample
int __builtin___sprintf_chk (char *s, int flag, size_t os, const char *fmt, 
...);
int __builtin___snprintf_chk (char *s, size_t maxlen, int flag, size_t os,
                              const char *fmt, ...);
int __builtin___vsprintf_chk (char *s, int flag, size_t os, const char *fmt,
                              va_list ap);
int __builtin___vsnprintf_chk (char *s, size_t maxlen, int flag, size_t os,
                               const char *fmt, va_list ap);
@end smallexample

The added @var{flag} argument is passed unchanged to @code{__sprintf_chk}
etc.@: functions and can contain implementation specific flags on what
additional security measures the checking function might take, such as
handling @code{%n} differently.

The @var{os} argument is the object size @var{s} points to, like in the
other built-in functions.  There is a small difference in the behavior
though, if @var{os} is @code{(size_t) -1}, the built-in functions are
optimized into the non-checking functions only if @var{flag} is 0, otherwise
the checking function is called with @var{os} argument set to
@code{(size_t) -1}.

In addition to this, there are checking built-in functions
@code{__builtin___printf_chk}, @code{__builtin___vprintf_chk},
@code{__builtin___fprintf_chk} and @code{__builtin___vfprintf_chk}.
These have just one additional argument, @var{flag}, right before
format string @var{fmt}.  If the compiler is able to optimize them to
@code{fputc} etc.@: functions, it will, otherwise the checking function
should be called and the @var{flag} argument passed to it.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]