From dd66e0282bf64bfb4132216ccdfeecb90d165954 Mon Sep 17 00:00:00 2001 From: msebor Date: Mon, 6 Feb 2017 20:11:51 +0000 Subject: [PATCH] PR tree-optimization/ 79376 - wrong lower bound with %s and non-constant strings in -Wformat-overflow gcc/ChangeLog: * gimple-fold.c (get_range_strlen): Set the minimum length to zero. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@245221 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 5 + gcc/gimple-fold.c | 5 +- gcc/testsuite/ChangeLog | 6 + .../gcc.dg/tree-ssa/builtin-sprintf-warn-14.c | 210 +++++++++++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/pr79376.c | 109 +++++++++++ 5 files changed, 333 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-14.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr79376.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a29c20e35ca..76dc97edd35 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2017-02-06 Martin Sebor + + PR tree-optimization/79376 + * gimple-fold.c (get_range_strlen): Set the minimum length to zero. + 2017-02-06 Uros Bizjak * config/i386/sse.md (vector modes -> vec_extract* splitter): Use diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 1cd22a8bfdd..a75dd917fe4 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -1244,8 +1244,9 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, return false; val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val, integer_one_node); - /* Avoid using the array size as the minimum. */ - minlen = NULL; + /* Set the minimum size to zero since the string in + the array could have zero length. */ + *minlen = ssize_int (0); } } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6737cf46e3c..b29d2835419 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2017-02-06 Martin Sebor + + PR tree-optimization/79376 + * gcc.dg/tree-ssa/builtin-sprintf-warn-14.c: New test. + * gcc.dg/tree-ssa/pr79376.c: Ditto. + 2017-02-06 Jakub Jelinek PR c++/79379 diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-14.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-14.c new file mode 100644 index 00000000000..dc3f132707b --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-14.c @@ -0,0 +1,210 @@ +/* PR middle-end/79376 - wrong lower bound with %s and non-constant + strings in -Wformat-overflow + { dg-do compile } + { dg-options "-O2 -Wall -Wformat-overflow=1 -ftrack-macro-expansion=0" } */ + +typedef __SIZE_TYPE__ size_t; + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-INT_MAX - 1) + +/* When debugging, define LINE to the line number of the test case to exercise + and avoid exercising any of the others. The buffer and objsize macros + below make use of LINE to avoid warnings for other lines. */ +#ifndef LINE +# define LINE 0 +#endif + +extern int int_value (void); + +int int_range (int min, int max) +{ + int n = int_value (); + return n < min || max < n ? min : n; +} + +const char* +choose_string (const char *s1, const char *s2, const char *s3) +{ + int i = int_value (); + return i < 0 ? s1 : 0 < i ? s3 : s2; +} + +void sink (char*, char*); + +int dummy_sprintf (char*, const char*, ...); + +char buffer [256]; +extern char *ptr; + +const char s0[] = ""; +const char s1[] = "1"; +const char s2[] = "12"; +const char s3[] = "123"; +const char s4[] = "1234"; +const char s5[] = "12345"; +const char s6[] = "123456"; +const char s7[] = "1234567"; +const char s8[] = "12345678"; +const char s9[] = "123456789"; +extern const char sx[]; +extern const char sy[]; + +/* Evaluate to an array of SIZE characters when non-negative, or to + a pointer to an unknown object otherwise. */ +#define buffer(size) \ + ((0 <= size) ? buffer + sizeof buffer - (size) : ptr) + +/* Helper to expand function to either __builtin_f or dummy_f to + make debugging GCC easy. */ +#define FUNC(f) \ + ((!LINE || LINE == __LINE__) ? __builtin_ ## f : dummy_ ## f) + +/* Macro to verify that calls to __builtin_sprintf (i.e., with no size + argument) issue diagnostics by correctly determining the size of + the destination buffer. */ +#define T(size, ...) \ + (FUNC (sprintf) (buffer (size), __VA_ARGS__), \ + sink (buffer, ptr)) + +/* Return a value in the range [MIN, MAX]. */ +#define R(min, max) int_range (min, max) + +/* Return one of the strings S1, S2, S3. */ +#define S(s1, s2, s3) choose_string (s1, s2, s3) + +struct S { + char a1[1]; + char a2[2]; + char a3[3]; + char a4[4]; + char a5[5]; + char ax[]; +}; + +void test_strings (struct S *s) +{ + T (0, "%-s", S (s->a1, s->a1, s1)); /* { dg-warning "writing up to 1 byte" } */ + T (0, "%-s", S (s->a1, s->a2, s1)); /* { dg-warning "writing up to 1 byte" } */ + T (0, "%-s", S (s->a2, s->a1, s1)); /* { dg-warning "writing up to 1 byte" } */ + + T (0, "%-s", S (s->a1, s1, s->a1)); /* { dg-warning "writing up to 1 byte" } */ + T (0, "%-s", S (s->a1, s1, s->a2)); /* { dg-warning "writing up to 1 byte" } */ + T (0, "%-s", S (s->a2, s1, s->a1)); /* { dg-warning "writing up to 1 byte" } */ + + T (0, "%-s", S (s1, s->a1, s1)); /* { dg-warning "writing up to 1 byte" } */ + T (0, "%-s", S (s1, s->a1, s1)); /* { dg-warning "writing up to 1 byte" } */ + T (0, "%-s", S (s1, s->a2, s1)); /* { dg-warning "writing up to 1 byte" } */ + + T (0, "%-s", S (s->a1, s->a1, s2)); /* { dg-warning "writing up to 2 bytes" } */ + T (0, "%-s", S (s->a1, s->a2, s2)); /* { dg-warning "writing up to 2 bytes" } */ + T (0, "%-s", S (s->a2, s->a1, s2)); /* { dg-warning "writing up to 2 bytes" } */ + + T (0, "%-s", S (s->a1, s2, s->a1)); /* { dg-warning "writing up to 2 bytes" } */ + T (0, "%-s", S (s->a1, s2, s->a2)); /* { dg-warning "writing up to 2 bytes" } */ + T (0, "%-s", S (s->a2, s2, s->a1)); /* { dg-warning "writing up to 2 bytes" } */ + + T (0, "%-s", S (s2, s->a1, s1)); /* { dg-warning "writing up to 2 bytes" } */ + T (0, "%-s", S (s2, s->a1, s1)); /* { dg-warning "writing up to 2 bytes" } */ + T (0, "%-s", S (s2, s->a2, s1)); /* { dg-warning "writing up to 2 bytes" } */ + + T (0, "%-s", S (s->a1, s->a1, s2)); /* { dg-warning "writing up to 2 bytes" } */ + T (0, "%-s", S (s->a1, s->a2, s2)); /* { dg-warning "writing up to 2 bytes" } */ + T (0, "%-s", S (s->a2, s->a1, s2)); /* { dg-warning "writing up to 2 bytes" } */ + + T (0, "%-s", S (s->a1, s2, s->a1)); /* { dg-warning "writing up to 2 bytes" } */ + T (0, "%-s", S (s->a1, s2, s->a2)); /* { dg-warning "writing up to 2 bytes" } */ + T (0, "%-s", S (s->a2, s2, s->a1)); /* { dg-warning "writing up to 2 bytes" } */ + + T (0, "%-s", S (s2, s->a1, s1)); /* { dg-warning "writing up to 2 bytes" } */ + T (0, "%-s", S (s3, s->a1, s2)); /* { dg-warning "writing up to 3 bytes" } */ + T (0, "%-s", S (s4, s->a2, s3)); /* { dg-warning "writing up to 4 bytes" } */ + + T (0, "%-s", S (s->a3, s->a5, s3)); /* { dg-warning "writing up to 4 bytes" } */ + T (0, "%-s", S (s->a3, s->a5, s3)); /* { dg-warning "writing up to 4 bytes" } */ + T (0, "%-s", S (s->a3, s->a5, s3)); /* { dg-warning "writing up to 4 bytes" } */ + + T (0, "%-s", S (s->a3, s3, s->a5)); /* { dg-warning "writing up to 4 bytes" } */ + T (0, "%-s", S (s->a5, s3, s->a3)); /* { dg-warning "writing up to 4 bytes" } */ + T (0, "%-s", S (s->a3, s3, s->a5)); /* { dg-warning "writing up to 4 bytes" } */ + + T (0, "%-s", S (s3, s->a1, s4)); /* { dg-warning "writing up to 4 bytes" } */ + T (0, "%-s", S (s4, s->a2, s3)); /* { dg-warning "writing up to 4 bytes" } */ +} + +void test_strings_with_width (struct S *s) +{ + T (0, "%*s", /* { dg-warning "writing up to 4 bytes" } */ + R (0, 1), + S (s->a3, s->a5, s3)); + + T (0, "%*s", /* { dg-warning "writing up to 4 bytes" } */ + R (0, 4), + S (s->a3, s->a5, s3)); + + T (0, "%*s", /* { dg-warning "writing between 1 and 4 bytes" } */ + R (1, 4), + S (s->a3, s->a5, s3)); + + T (0, "%*s", /* { dg-warning "writing between 2 and 4 bytes" } */ + R (2, 4), + S (s->a3, s->a5, s3)); + + T (0, "%*s", /* { dg-warning "writing between 3 and 4 bytes" } */ + R (3, 4), + S (s->a3, s->a5, s3)); + + T (0, "%*s", /* { dg-warning "writing between 3 and 5 bytes" } */ + R (3, 5), + S (s->a3, s->a5, s3)); + + T (0, "%*s", /* { dg-warning "writing between 3 and 9 bytes" } */ + R (3, 9), + S (s->a3, s->a5, s3)); + + T (3, "%*s", /* { dg-warning "writing between 3 and 9 bytes" } */ + R (3, 9), + S (s->a3, s->a5, s3)); + + /* The longest string fits but the terminating nul will overflow. Since + the null won't overflow with the shorter strings the warning is a "may + write." */ + T (4, "%*s", /* { dg-warning "may write a terminating nul" } */ + R (3, 9), + S (s->a3, s->a5, s3)); +} + +void test_strings_with_width_and_precisin (struct S *s) +{ + T (0, "%*.*s", /* { dg-warning "writing up to 1 byte" } */ + R (0, 1), + R (0, 1), + S (s->a3, s->a5, s3)); + + T (0, "%*.*s", /* { dg-warning "writing up to 2 bytes" } */ + R (0, 2), + R (0, 1), + S (s->a3, s->a5, s3)); + + T (0, "%*.*s", /* { dg-warning "writing up to 9 bytes" } */ + R (0, 9), + R (0, 1), + S (s->a3, s->a5, s3)); + + /* Since the longest string/array fits there is no warning even if + the maximum width would cause overflow. */ + T (6, "%*.*s", + R (0, 9), + R (0, 1), + S (s->a3, s->a5, s3)); + + T (0, "%*.*s", /* { dg-warning "writing up to 2 bytes" } */ + R (0, 2), + R (0, 2), + S (s->a3, s->a5, s3)); + + T (0, "%*.*s", /* { dg-warning "writing up to 3 bytes" } */ + R (0, 3), + R (0, 2), + S (s->a3, s->a5, s3)); +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79376.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79376.c new file mode 100644 index 00000000000..838bfe0508e --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr79376.c @@ -0,0 +1,109 @@ +/* PR tree-optimization/79376 - wrong lower bound with %s and non-constant + strings in -Wformat-overflow + { dg-compile } + { dg-options "-O2 -fdump-tree-optimized" } */ + +#define CAT(s, n) s ## n +#define FAIL(line) CAT (failure_on_line_, line) + +/* Emit a call to a function named failure_on_line_NNN when EXPR is false. */ +#define ASSERT(expr) \ + do { \ + extern void FAIL (__LINE__)(void); \ + if (!(expr)) FAIL (__LINE__)(); \ + } while (0) + +#define KEEP(line) CAT (keep_call_on_line_, line) + +/* Emit a call to a function named keep_call_on_line_NNN when EXPR is true. + Used to verify that the expression need not be the only one that holds. */ +#define ASSERT_MAYBE(expr) \ + do { \ + extern void KEEP (__LINE__)(void); \ + if (expr) KEEP (__LINE__)(); \ + } while (0) + +struct Arrays +{ + char a1[1]; + char a2[2]; + char a3[3]; + char a4[4]; + char a5[5]; + char ax[]; +}; + +void test_arrays (int i, struct Arrays *a) +{ + { + const char *s = i < 0 ? a->a3 : a->a1; + + int n = __builtin_snprintf (0, 0, "%-s", s); + + ASSERT (0 <= n && n < 3); + + ASSERT_MAYBE (0 == n); + ASSERT_MAYBE (1 == n); + ASSERT_MAYBE (2 == n); + } + + { + const char *s = i < 0 ? a->a3 : a->a5; + + int n = __builtin_snprintf (0, 0, "%-s", s); + + ASSERT (0 <= n && n < 5); + + ASSERT_MAYBE (0 == n); + ASSERT_MAYBE (1 == n); + ASSERT_MAYBE (2 == n); + ASSERT_MAYBE (3 == n); + ASSERT_MAYBE (4 == n); + } +} + +void test_string_and_array (int i, struct Arrays *a) +{ + { + const char *s = i < 0 ? a->a3 : "1"; + + int n = __builtin_snprintf (0, 0, "%-s", s); + + ASSERT (0 <= n && n < 3); + + ASSERT_MAYBE (0 == n); + ASSERT_MAYBE (1 == n); + ASSERT_MAYBE (2 == n); + } + + { + const char *s = i < 0 ? "12" : a->a5; + + int n = __builtin_snprintf (0, 0, "%-s", s); + + ASSERT (0 <= n && n < 5); + + ASSERT_MAYBE (0 == n); + ASSERT_MAYBE (1 == n); + ASSERT_MAYBE (2 == n); + ASSERT_MAYBE (3 == n); + ASSERT_MAYBE (4 == n); + } + + { + const char *s = i < 0 ? a->a4 : 0 < i ? "12" : a->a5; + + int n = __builtin_snprintf (0, 0, "%-s", s); + + ASSERT (0 <= n && n < 5); + + ASSERT_MAYBE (0 == n); + ASSERT_MAYBE (1 == n); + ASSERT_MAYBE (2 == n); + ASSERT_MAYBE (3 == n); + ASSERT_MAYBE (4 == n); + } +} + +/* { dg-final { scan-tree-dump-not "failure_on_line" "optimized"} } + { dg-final { scan-tree-dump-times "keep_call_on_line" 21 "optimized"} } */ -- 2.11.4.GIT