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.