From eb07c7cffb2ca361e793281ca142b2f8063aa0e1 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Tue, 10 Jan 2017 14:54:15 -0700 Subject: [PATCH] PR middle-end/78245 - missing -Wformat-length on an overflow of a dynamically allocated buffer gcc/testsuite/ChangeLog: PR middle-end/78245 * gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: Add tests. gcc/ChangeLog: PR middle-end/78245 * gimple-ssa-sprintf.c (get_destination_size): Call {init,fini}object_sizes. * tree-object-size.c (addr_object_size): Adjust. (pass_through_call): Adjust. (pass_object_sizes::execute): Adjust. * tree-object-size.h (fini_object_sizes): Declare. From-SVN: r244293 --- gcc/gimple-ssa-sprintf.c | 5 + .../gcc.dg/tree-ssa/builtin-sprintf-warn-3.c | 124 ++++++++++++++++-- gcc/tree-object-size.c | 2 +- gcc/tree-object-size.h | 1 + 4 files changed, 118 insertions(+), 14 deletions(-) diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 166b34ba37e..9b327f67fa9 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -2723,6 +2723,9 @@ get_destination_size (tree dest) a member array as opposed to the whole enclosing object), otherwise use type-zero object size to determine the size of the enclosing object (the function fails without optimization in this type). */ + + init_object_sizes (); + int ost = optimize > 0; unsigned HOST_WIDE_INT size; if (compute_builtin_object_size (dest, ost, &size)) @@ -3120,6 +3123,8 @@ pass_sprintf_length::execute (function *fun) } } + fini_object_sizes (); + return 0; } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-3.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-3.c index 1ca0a66388e..eb5978180f1 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-3.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-3.c @@ -1,5 +1,10 @@ -/* { dg-do compile } */ -/* { dg-options "-O2 -Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */ +/* Verify that all sprintf built-ins detect overflow involving directives + with non-constant arguments known to be constrained by some range of + values, and even when writing into dynamically allocated buffers. + -O2 (-ftree-vrp) is necessary for the tests involving ranges to pass, + otherwise -O1 is sufficient. + { dg-do compile } + { dg-options "-O2 -Wformat -Wformat-length=1 -ftrack-macro-expansion=0" } */ typedef __SIZE_TYPE__ size_t; @@ -9,18 +14,26 @@ typedef __SIZE_TYPE__ size_t; #define bos(x) __builtin_object_size (x, 0) -#define T(bufsize, fmt, ...) \ - do { \ - if (!LINE || __LINE__ == LINE) \ - { \ - char *d = (char *)__builtin_malloc (bufsize); \ - __builtin___sprintf_chk (d, 0, bos (d), fmt, __VA_ARGS__); \ - sink (d); \ - } \ - } while (0) +/* Defined (and redefined) to the allocation function to use, either + malloc, or alloca, or a VLA. */ +#define ALLOC(p, n) (p) = __builtin_malloc (n) -void -sink (void*); +/* Defined (and redefined) to the sprintf function to exercise. */ +#define TEST_SPRINTF(d, maxsize, objsize, fmt, ...) \ + __builtin___sprintf_chk (d, 0, objsize, fmt, __VA_ARGS__) + +#define T(bufsize, fmt, ...) \ + do { \ + if (!LINE || __LINE__ == LINE) \ + { \ + char *d; \ + ALLOC (d, bufsize); \ + TEST_SPRINTF (d, 0, bos (d), fmt, __VA_ARGS__); \ + sink (d); \ + } \ + } while (0) + +void sink (void*); /* Identity function to verify that the checker figures out the value of the operand even when it's not constant (i.e., makes use of @@ -362,3 +375,88 @@ void test_too_large (char *d, int x, __builtin_va_list va) __builtin___vsnprintf_chk (d, ptrmax_m1, 0, ptrmax_m1, "%c", va); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX - 1" { target lp64 } } */ __builtin___vsnprintf_chk (d, ptrmax, 0, ptrmax, "%c", va); /* { dg-warning "specified bound \[0-9\]+ exceeds .INT_MAX." "PTRDIFF_MAX" { target lp64 } } */ } + +/* Exercise ordinary sprintf with malloc. */ +#undef TEST_SPRINTF +#define TEST_SPRINTF(d, maxsize, objsize, fmt, ...) \ + __builtin_sprintf (d, fmt, __VA_ARGS__) + +void test_sprintf_malloc (const char *s, const char *t) +{ +#define x x () + + T (1, "%-s", x ? "" : "1"); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? "1" : ""); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? s : "1"); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? "1" : s); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? s : t); + + T (2, "%-s", x ? "" : "1"); + T (2, "%-s", x ? "" : s); + T (2, "%-s", x ? "1" : ""); + T (2, "%-s", x ? s : ""); + T (2, "%-s", x ? "1" : "2"); + T (2, "%-s", x ? "" : "12"); /* { dg-warning "nul past the end" } */ + T (2, "%-s", x ? "12" : ""); /* { dg-warning "nul past the end" } */ + + T (2, "%-s", x ? "" : "123"); /* { dg-warning "into a region" } */ + T (2, "%-s", x ? "123" : ""); /* { dg-warning "into a region" } */ + +#undef x +} + +/* Exercise ordinary sprintf with alloca. */ +#undef ALLOC +#define ALLOC(p, n) (p) = __builtin_alloca (n) + +void test_sprintf_alloca (const char *s, const char *t) +{ +#define x x () + + T (1, "%-s", x ? "" : "1"); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? "1" : ""); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? s : "1"); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? "1" : s); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? s : t); + + T (2, "%-s", x ? "" : "1"); + T (2, "%-s", x ? "" : s); + T (2, "%-s", x ? "1" : ""); + T (2, "%-s", x ? s : ""); + T (2, "%-s", x ? "1" : "2"); + T (2, "%-s", x ? "" : "12"); /* { dg-warning "nul past the end" } */ + T (2, "%-s", x ? "12" : ""); /* { dg-warning "nul past the end" } */ + + T (2, "%-s", x ? "" : "123"); /* { dg-warning "into a region" } */ + T (2, "%-s", x ? "123" : ""); /* { dg-warning "into a region" } */ + +#undef x +} + +/* Exercise ordinary sprintf with a VLA. */ +#undef ALLOC +#define ALLOC(p, n) char vla [i (n)]; (p) = vla + +void test_sprintf_vla (const char *s, const char *t) +{ +#define x x () + + T (1, "%-s", x ? "" : "1"); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? "1" : ""); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? s : "1"); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? "1" : s); /* { dg-warning "nul past the end" } */ + T (1, "%-s", x ? s : t); + + T (2, "%-s", x ? "" : "1"); + T (2, "%-s", x ? "" : s); + T (2, "%-s", x ? "1" : ""); + T (2, "%-s", x ? s : ""); + T (2, "%-s", x ? "1" : "2"); + T (2, "%-s", x ? "" : "12"); /* { dg-warning "nul past the end" } */ + T (2, "%-s", x ? "12" : ""); /* { dg-warning "nul past the end" } */ + + T (2, "%-s", x ? "" : "123"); /* { dg-warning "into a region" } */ + T (2, "%-s", x ? "123" : ""); /* { dg-warning "into a region" } */ + +#undef x +} diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c index 71ec7b28777..eb08b33316c 100644 --- a/gcc/tree-object-size.c +++ b/gcc/tree-object-size.c @@ -1235,7 +1235,7 @@ init_object_sizes (void) /* Destroy data structures after the object size computation. */ -static void +void fini_object_sizes (void) { int object_size_type; diff --git a/gcc/tree-object-size.h b/gcc/tree-object-size.h index 500a8343292..1b558057329 100644 --- a/gcc/tree-object-size.h +++ b/gcc/tree-object-size.h @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #define GCC_TREE_OBJECT_SIZE_H extern void init_object_sizes (void); +extern void fini_object_sizes (void); extern bool compute_builtin_object_size (tree, int, unsigned HOST_WIDE_INT *); #endif // GCC_TREE_OBJECT_SIZE_H