From ebee1eb978f5168267f8ee9dc530f620d5639943 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Fri, 3 Feb 2017 02:18:59 +0000 Subject: [PATCH] PR middle-end/79275 - -Wformat-overflow false positive exceeding INT_MAX in glibc sysdeps/posix/tempname.c gcc/testsuite/ChangeLog: PR middle-end/79275 * gcc.dg/tree-ssa/builtin-sprintf-warn-11.c: New test. * gcc.dg/tree-ssa/pr79275.c: New test. gcc/ChangeLog: PR middle-end/79275 * gimple-ssa-sprintf.c (get_string_length): Set lower bound to zero. (format_string): Tighten up the range of output for non-constant strings and correct the expected range for wide non-constant strings. From-SVN: r245142 --- gcc/ChangeLog | 7 + gcc/gimple-ssa-sprintf.c | 91 +++-- gcc/testsuite/ChangeLog | 6 + .../gcc.dg/tree-ssa/builtin-sprintf-warn-11.c | 323 ++++++++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/pr79275.c | 14 + 5 files changed, 419 insertions(+), 22 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-11.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr79275.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index c7ac7ee9ceb..fe36a9e0f7b 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2017-02-02 Martin Sebor + + PR middle-end/79275 + * gimple-ssa-sprintf.c (get_string_length): Set lower bound to zero. + (format_string): Tighten up the range of output for non-constant + strings and correct the expected range for wide non-constant strings. + 2017-02-02 Martin Sebor * doc/invoke.texi (-maccumulate-args): Fix bad grammar. diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 11f41741f95..9e099f0a133 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -1832,10 +1832,11 @@ get_string_length (tree str) } else { - /* When the upper bound is unknown (as assumed to be excessive) + /* When the upper bound is unknown (it can be zero or excessive) set the likely length to the greater of 1 and the length of - the shortest string. */ + the shortest string and reset the lower bound to zero. */ res.range.likely = res.range.min ? res.range.min : warn_level > 1; + res.range.min = 0; } res.range.unlikely = res.range.max; @@ -1986,43 +1987,89 @@ format_string (const directive &dir, tree arg) } else { - /* For a '%s' and '%ls' directive with a non-constant string, - the minimum number of characters is the greater of WIDTH - and either 0 in mode 1 or the smaller of PRECISION and 1 - in mode 2, and the maximum is PRECISION or -1 to disable - tracking. */ + /* For a '%s' and '%ls' directive with a non-constant string (either + one of a number of strings of known length or an unknown string) + the minimum number of characters is lesser of PRECISION[0] and + the length of the shortest known string or zero, and the maximum + is the lessser of the length of the longest known string or + PTRDIFF_MAX and PRECISION[1]. The likely length is either + the minimum at level 1 and the greater of the minimum and 1 + at level 2. This result is adjust upward for width (if it's + specified). */ + + if (dir.modifier == FMT_LEN_l) + { + /* A wide character converts to as few as zero bytes. */ + slen.range.min = 0; + if (slen.range.max < target_int_max ()) + slen.range.max *= target_mb_len_max (); + + if (slen.range.likely < target_int_max ()) + slen.range.likely *= 2; + + if (slen.range.likely < target_int_max ()) + slen.range.unlikely *= target_mb_len_max (); + } + + res.range = slen.range; if (dir.prec[0] >= 0) { + /* Adjust the minimum to zero if the string length is unknown, + or at most the lower bound of the precision otherwise. */ if (slen.range.min >= target_int_max ()) - slen.range.min = 0; + res.range.min = 0; else if ((unsigned HOST_WIDE_INT)dir.prec[0] < slen.range.min) - { - slen.range.min = dir.prec[0]; - slen.range.likely = slen.range.min; - } + res.range.min = dir.prec[0]; + /* Make both maxima no greater than the upper bound of precision. */ if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max || slen.range.max >= target_int_max ()) { - slen.range.max = dir.prec[1]; - slen.range.likely = slen.range.max; + res.range.max = dir.prec[1]; + res.range.unlikely = dir.prec[1]; } + + /* If precision is constant, set the likely counter to the lesser + of it and the maximum string length. Otherwise, if the lower + bound of precision is greater than zero, set the likely counter + to the minimum. Otherwise set it to zero or one based on + the warning level. */ + if (dir.prec[0] == dir.prec[1]) + res.range.likely + = ((unsigned HOST_WIDE_INT)dir.prec[0] < slen.range.max + ? dir.prec[0] : slen.range.max); + else if (dir.prec[0] > 0) + res.range.likely = res.range.min; + else + res.range.likely = warn_level > 1; + } + else if (dir.prec[1] >= 0) + { + res.range.min = 0; + if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max) + res.range.max = dir.prec[1]; + res.range.likely = dir.prec[1] ? warn_level > 1 : 0; } else if (slen.range.min >= target_int_max ()) { - slen.range.min = 0; - slen.range.max = HOST_WIDE_INT_MAX; - /* At level one strings of unknown length are assumed to be + res.range.min = 0; + res.range.max = HOST_WIDE_INT_MAX; + /* At level 1 strings of unknown length are assumed to be empty, while at level 1 they are assumed to be one byte long. */ - slen.range.likely = warn_level > 1; + res.range.likely = warn_level > 1; + } + else + { + /* A string of unknown length unconstrained by precision is + assumed to be empty at level 1 and just one character long + at higher levels. */ + if (res.range.likely >= target_int_max ()) + res.range.likely = warn_level > 1; } - slen.range.unlikely = slen.range.max; - - res.range = slen.range; - res.knownrange = slen.knownrange; + res.range.unlikely = res.range.max; } /* Bump up the byte counters if WIDTH is greater. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 07ff543e6a1..1289892dada 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2017-02-02 Martin Sebor + + PR middle-end/79275 + * gcc.dg/tree-ssa/builtin-sprintf-warn-11.c: New test. + * gcc.dg/tree-ssa/pr79275.c: New test. + 2017-02-02 Aaron Sawdey * gcc.dg/sms-8.c: Update options for powerpc*-*-*. diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-11.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-11.c new file mode 100644 index 00000000000..b714c952a73 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-11.c @@ -0,0 +1,323 @@ +/* PR middle-end/79275 - -Wformat-overflow false positive exceeding INT_MAX + in glibc sysdeps/posix/tempname.c + { dg-do compile } + { dg-options "-O2 -Wall -Wformat-overflow=1 -ftrack-macro-expansion=0" } */ + +typedef __SIZE_TYPE__ size_t; +typedef __WCHAR_TYPE__ wchar_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); +extern size_t size_value (void); + +int int_range (int min, int max) +{ + int n = int_value (); + return n < min || max < n ? min : n; +} + +size_t size_range (size_t min, size_t max) +{ + size_t n = size_value (); + return n < min || max < n ? min : n; +} + +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[]; + +/* Wide string literals outside the ASCII range to avoid assumptions + about the number of narrow characters they might convert to beyond + up to 6 bytes each (the maximum for UTF-8 not exceeded by any known + encoding). */ +const wchar_t ws0[] = L""; +const wchar_t ws1[] = L"\u1111"; +const wchar_t ws2[] = L"\u1111\u2222"; +const wchar_t ws3[] = L"\u1111\u2222\u3333"; +const wchar_t ws4[] = L"\u1111\u2222\u3333\u4444"; +const wchar_t ws5[] = L"\u1111\u2222\u3333\u4444\u5555"; +const wchar_t ws6[] = L"\u1111\u2222\u3333\u4444\u5555\u6666"; +const wchar_t ws7[] = L"\u1111\u2222\u3333\u4444\u5555\u6666\u7777"; +const wchar_t ws8[] = + L"\u1111\u2222\u3333\u4444\u5555\u6666\u7777\u8888"; +const wchar_t ws9[] = + L"\u1111\u2222\u3333\u4444\u5555\u6666\u7777\u8888\u9999"; +extern const wchar_t wsx[]; +extern const wchar_t wsy[]; + +static const int imin = INT_MIN; +static const int imax = INT_MAX; + +/* 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 IR(min, max) int_range (min, max) + +/* Return a string whose length is in the range [MIN, MAX] where + both MIN and MAX must be digits in the range [0, 9]. */ +#define SR(min, max) (int_value () < 0 ? s##min : s##max) + +/* Return a wide string whose length is in the range [MIN, MAX] where + both MIN and MAX must be digits in the range [0, 9]. */ +#define WR(min, max) (int_value () < 0 ? ws##min : ws##max) + +void test_narrow_string_with_precision (void) +{ + T (-1, "%.*s", IR ( 0, 1), SR (0, 1)); + T (-1, "%.*s", IR ( 0, 1), SR (0, 2)); + T (-1, "%.*s", IR ( 0, 1), SR (0, 3)); + T (-1, "%.*s", IR ( 0, 1), SR (0, 4)); + T (-1, "%.*s", IR ( 0, 1), SR (0, 9)); + T (-1, "%.*s", IR ( 0, 2), SR (0, 9)); + T (-1, "%.*s", IR ( 0, 3), SR (0, 9)); + T (-1, "%.*s", IR ( 0, 4), SR (0, 9)); + T (-1, "%.*s", IR ( 0, 9), SR (0, 9)); + T (-1, "%.*s", IR ( 0, 99), SR (0, 9)); + T (-1, "%.*s", IR ( 0, 99), SR (0, x)); + T (-1, "%.*s", IR ( 0, 99), SR (1, x)); + T (-1, "%.*s", IR ( 0, 99), SR (x, 1)); + T (-1, "%.*s", IR ( 0, 99), SR (x, 9)); + + T (-1, "%.*s", IR (imax / 3, imax / 2), SR (x, y)); + + /* Non-constant zero length string. */ + T ( 0, "%.*s", IR (imin, -1), SR (0, 0)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*s", IR (imin, 0), SR (0, 0)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*s", IR (-1, 0), SR (0, 0)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*s", IR (-1, 1), SR (0, 0)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*s", IR (-1, 99), SR (0, 0)); /* { dg-warning "writing a terminating nul" } */ + + /* String with length between 0 and 1 character. */ + T ( 0, "%.*s", IR (imin, -1), SR (0, 1)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*s", IR (imin, 0), SR (0, 1)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*s", IR (-2, -1), SR (0, 1)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*s", IR (-2, 0), SR (0, 1)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*s", IR ( 0, 1), SR (0, 1)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*s", IR ( 0, 2), SR (0, 1)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*s", IR ( 0, 99), SR (0, 1)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*s", IR ( 0, imax), SR (0, 1)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*s", IR ( 1, imax), SR (0, 1)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*s", IR ( 9, imax), SR (0, 1)); /* { dg-warning "writing up to 1 byte" } */ + + /* String with length between 2 and 3 characters. */ + T ( 0, "%.*s", IR (imin, -1), SR (2, 3)); /* { dg-warning "writing between 2 and 3 bytes" } */ + T ( 0, "%.*s", IR (imin, 0), SR (2, 3)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*s", IR (-2, -1), SR (2, 3)); /* { dg-warning "writing between 2 and 3 bytes" } */ + T ( 0, "%.*s", IR (-2, 0), SR (2, 3)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*s", IR (-2, 1), SR (2, 3)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*s", IR ( 0, 1), SR (2, 3)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*s", IR ( 0, 2), SR (2, 3)); /* { dg-warning "writing up to 2 bytes" } */ + T ( 0, "%.*s", IR ( 0, 99), SR (2, 3)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%.*s", IR ( 0, imax), SR (2, 3)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%.*s", IR ( 1, 99), SR (2, 3)); /* { dg-warning "writing between 1 and 3 bytes" } */ + T ( 0, "%.*s", IR ( 9, 99), SR (2, 3)); /* { dg-warning "writing between 2 and 3 bytes" } */ + + T ( 0, "%.*s", IR ( 0, 1), SR (0, 9)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*s", IR ( 0, 2), SR (0, 9)); /* { dg-warning "writing up to 2 bytes" } */ + T ( 0, "%.*s", IR ( 0, 9), SR (0, 9)); /* { dg-warning "writing up to 9 bytes" } */ + T ( 0, "%.*s", IR ( 0, 79), SR (0, 9)); /* { dg-warning "writing up to 9 bytes" } */ + T ( 0, "%.*s", IR ( 1, 2), SR (0, 9)); /* { dg-warning "writing up to 2 bytes" } */ + T ( 0, "%.*s", IR ( 2, 3), SR (0, 9)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%.*s", IR ( 7, 13), SR (0, 9)); /* { dg-warning "writing up to 9 bytes" } */ + + /* String between N and unknown number of characters long. */ + T ( 0, "%.*s", IR (imin, -1), SR (0, x)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*s", IR (imin, -1), SR (1, x)); /* { dg-warning "writing likely 1 or more bytes" } */ + T ( 1, "%.*s", IR (imin, -1), SR (1, x)); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*s", IR (imin, -1), SR (8, x)); /* { dg-warning "writing likely 8 or more bytes" } */ + T ( 1, "%.*s", IR (imin, -1), SR (x, 9)); /* { dg-warning "writing likely 9 or more bytes" } */ + + /* Unknown strings. */ + T ( 1, "%.*s", IR (imin, -1), SR (x, y)); + T ( 1, "%.*s", IR (imin, 0), SR (x, y)); + T ( 1, "%.*s", IR ( -99, 1), SR (x, y)); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*s", IR ( -2, 2), SR (x, y)); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*s", IR ( -1, 99), SR (x, y)); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*s", IR ( 0, 99), SR (x, y)); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*s", IR ( 1, 99), SR (x, y)); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*s", IR ( 9, 99), SR (x, y)); /* { dg-warning "may write a terminating nul" } */ +} + +void test_narrow_string_with_width_and_precision (void) +{ + T (-1, "%*.*s", IR ( 0, 1), IR ( 0, 1), SR (0, 1)); + T (-1, "%*.*s", IR ( 0, 1), IR ( 0, 1), SR (0, 2)); + T (-1, "%*.*s", IR ( 0, 1), IR ( 0, 1), SR (0, 3)); + T (-1, "%*.*s", IR ( 0, 1), IR ( 0, 1), SR (0, 4)); + T (-1, "%*.*s", IR ( 0, 1), IR ( 0, 1), SR (0, 9)); + T (-1, "%*.*s", IR ( 0, 2), IR ( 0, 2), SR (0, 9)); + T (-1, "%*.*s", IR ( 0, 3), IR ( 0, 3), SR (0, 9)); + T (-1, "%*.*s", IR ( 0, 4), IR ( 0, 4), SR (0, 9)); + T (-1, "%*.*s", IR ( 0, 9), IR ( 0, 9), SR (0, 9)); + T (-1, "%*.*s", IR ( 0, 99), IR ( 0, 99), SR (0, 9)); + T (-1, "%*.*s", IR ( 0, 99), IR ( 0, 99), SR (0, x)); + T (-1, "%*.*s", IR ( 0, 99), IR ( 0, 99), SR (1, x)); + T (-1, "%*.*s", IR ( 0, 99), IR ( 0, 99), SR (x, 1)); + T (-1, "%*.*s", IR ( 0, 99), IR ( 0, 99), SR (x, 9)); + T (-1, "%*.*s", IR (12, 34), IR (45, 67), SR (x, 9)); + T (-1, "%*.*s", IR (12, 34), IR (45, 67), SR (x, y)); + + T (-1, "%*.*s", IR (imax / 5, imax / 4), IR (imax / 3, imax / 2), SR (x, y)); + + T (-1, "%*.*s %*.*s", + IR (imax / 9, imax / 8), IR (imax / 7, imax / 6), SR (x, y), + IR (imax / 5, imax / 4), IR (imax / 3, imax / 2), SR (x, y)); + + /* The two directives below combined convert to [INT_MAX, INT_MAX + 1]. + Since the lower end of the range doesn't exceed INT_MAX no warning + is expected. */ + T (-1, "%*.*s%*.*s", + IR (imax - 5, imax - 3), IR (1, 2), SR (x, y), + IR ( 5, 6), IR (3, 4), SR (x, y)); + + /* The three directives below (the two %s plus the space in between) + combined convert to [INT_MAX + 1, INT_MAX + 2]. Since the lower + end of the range exceeds INT_MAX a warning is expected. */ + T (-1, "%*.*s %*.*s", /* { dg-warning "INT_MAX" } */ + IR (imax - 5, imax - 3), IR (1, 2), SR (x, y), + IR ( 5, 6), IR (3, 4), SR (x, y)); + + /* Non-constant zero length string. */ + T ( 0, "%*.*s", IR ( 0, 1), IR (imin, -1), SR (0, 0)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%*.*s", IR ( 0, 2), IR (imin, -1), SR (0, 0)); /* { dg-warning "writing up to 2 bytes" } */ + T ( 0, "%*.*s", IR ( 0, 3), IR (imin, -1), SR (0, 0)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%*.*s", IR ( 0, 3), IR ( 0, 1), SR (0, 0)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%*.*s", IR ( 0, 3), IR ( 0, 1), SR (0, 1)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%*.*s", IR ( 0, 3), IR ( 0, 2), SR (0, 1)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%*.*s", IR ( 0, 3), IR ( 0, 3), SR (0, 1)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%*.*s", IR ( 0, 3), IR ( 0, 1), SR (3, 5)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%*.*s", IR ( 0, 3), IR ( 0, 2), SR (3, 5)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%*.*s", IR ( 0, 3), IR ( 0, 3), SR (3, 5)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%*.*s", IR ( 0, 3), IR ( 0, 4), SR (3, 5)); /* { dg-warning "writing up to 4 bytes" } */ + T ( 0, "%*.*s", IR ( 0, 3), IR ( 0, 5), SR (3, 5)); /* { dg-warning "writing up to 5 bytes" } */ + T ( 0, "%*.*s", IR ( 0, 3), IR ( 0, 6), SR (3, 5)); /* { dg-warning "writing up to 5 bytes" } */ + + T ( 0, "%*.*s", IR ( 1, 2), IR ( 0, 1), SR (3, 5)); /* { dg-warning "writing between 1 and 2 bytes" } */ + T ( 0, "%*.*s", IR ( 1, 2), IR ( 0, 2), SR (3, 5)); /* { dg-warning "writing between 1 and 2 bytes" } */ + T ( 0, "%*.*s", IR ( 1, 2), IR ( 0, 3), SR (3, 5)); /* { dg-warning "writing between 1 and 3 bytes" } */ + T ( 0, "%*.*s", IR ( 1, 2), IR ( 0, 4), SR (3, 5)); /* { dg-warning "writing between 1 and 4 bytes" } */ + T ( 0, "%*.*s", IR ( 1, 2), IR ( 0, 5), SR (3, 5)); /* { dg-warning "writing between 1 and 5 bytes" } */ + T ( 0, "%*.*s", IR ( 1, 2), IR ( 0, 6), SR (3, 5)); /* { dg-warning "writing between 1 and 5 bytes" } */ + T ( 0, "%*.*s", IR ( 2, 3), IR ( 0, 6), SR (3, 5)); /* { dg-warning "writing between 2 and 5 bytes" } */ + T ( 0, "%*.*s", IR ( 2, 3), IR ( 1, 6), SR (3, 5)); /* { dg-warning "writing between 2 and 5 bytes" } */ + T ( 0, "%*.*s", IR ( 2, 3), IR ( 2, 6), SR (3, 5)); /* { dg-warning "writing between 2 and 5 bytes" } */ + T ( 0, "%*.*s", IR ( 2, 3), IR ( 3, 6), SR (3, 5)); /* { dg-warning "writing between 3 and 5 bytes" } */ + T ( 0, "%*.*s", IR ( 2, 3), IR ( 4, 6), SR (3, 5)); /* { dg-warning "writing between 3 and 5 bytes" } */ + T ( 0, "%*.*s", IR ( 2, 3), IR ( 5, 6), SR (3, 5)); /* { dg-warning "writing between 3 and 5 bytes" } */ +} + +void test_wide_string (void) +{ + T (-1, "%.*ls", IR ( 0, 1), WR (0, 1)); + T (-1, "%.*ls", IR ( 0, 1), WR (0, 2)); + T (-1, "%.*ls", IR ( 0, 1), WR (0, 3)); + T (-1, "%.*ls", IR ( 0, 1), WR (0, 4)); + T (-1, "%.*ls", IR ( 0, 1), WR (0, 9)); + T (-1, "%.*ls", IR ( 0, 2), WR (0, 9)); + T (-1, "%.*ls", IR ( 0, 3), WR (0, 9)); + T (-1, "%.*ls", IR ( 0, 4), WR (0, 9)); + T (-1, "%.*ls", IR ( 0, 9), WR (0, 9)); + T (-1, "%.*ls", IR ( 0, 99), WR (0, 9)); + T (-1, "%.*ls", IR ( 0, 99), WR (0, x)); + T (-1, "%.*ls", IR ( 0, 99), WR (1, x)); + T (-1, "%.*ls", IR ( 0, 99), WR (x, 1)); + T (-1, "%.*ls", IR ( 0, 99), WR (x, 9)); + + /* Non-constant zero length string. */ + T ( 0, "%.*ls", IR (imin, -1), WR (0, 0)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*ls", IR (imin, 0), WR (0, 0)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*ls", IR (-1, 0), WR (0, 0)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*ls", IR (-1, 1), WR (0, 0)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*ls", IR (-1, 99), WR (0, 0)); /* { dg-warning "writing a terminating nul" } */ + + /* String with length between 0 and 1 character. */ + T ( 0, "%.*ls", IR (imin, -1), WR (0, 1)); /* { dg-warning "writing up to 6 bytes" } */ + T ( 0, "%.*ls", IR (imin, 0), WR (0, 1)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*ls", IR (-2, -1), WR (0, 1)); /* { dg-warning "writing up to 6 bytes" } */ + T ( 0, "%.*ls", IR (-2, 0), WR (0, 1)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*ls", IR ( 0, 1), WR (0, 1)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*ls", IR ( 0, 2), WR (0, 1)); /* { dg-warning "writing up to 2 bytes" } */ + T ( 0, "%.*ls", IR ( 0, 99), WR (0, 1)); /* { dg-warning "writing up to 6 bytes" } */ + T ( 0, "%.*ls", IR ( 0, imax), WR (0, 1)); /* { dg-warning "writing up to 6 bytes" } */ + T ( 0, "%.*ls", IR ( 1, imax), WR (0, 1)); /* { dg-warning "writing up to 6 bytes" } */ + T ( 0, "%.*ls", IR ( 9, imax), WR (0, 1)); /* { dg-warning "writing up to 6 bytes" } */ + + /* String with length between 2 and 3 characters. */ + T ( 0, "%.*ls", IR (imin, -1), WR (2, 3)); /* { dg-warning "writing up to 18 bytes" } */ + T ( 0, "%.*ls", IR (imin, 0), WR (2, 3)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*ls", IR (-2, -1), WR (2, 3)); /* { dg-warning "writing up to 18 bytes" } */ + T ( 0, "%.*ls", IR (-2, 0), WR (2, 3)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*ls", IR (-2, 1), WR (2, 3)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*ls", IR ( 0, 1), WR (2, 3)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*ls", IR ( 0, 2), WR (2, 3)); /* { dg-warning "writing up to 2 bytes" } */ + T ( 0, "%.*ls", IR ( 0, 99), WR (2, 3)); /* { dg-warning "writing up to 18 bytes" } */ + T ( 0, "%.*ls", IR ( 0, imax), WR (2, 3)); /* { dg-warning "writing up to 18 bytes" } */ + T ( 0, "%.*ls", IR ( 1, 99), WR (2, 3)); /* { dg-warning "writing up to 18 bytes" } */ + T ( 0, "%.*ls", IR ( 9, 99), WR (2, 3)); /* { dg-warning "writing up to 18 bytes" } */ + + T ( 0, "%.*ls", IR ( 0, 1), WR (0, 9)); /* { dg-warning "writing up to 1 byte" } */ + T ( 0, "%.*ls", IR ( 0, 2), WR (0, 9)); /* { dg-warning "writing up to 2 bytes" } */ + T ( 0, "%.*ls", IR ( 0, 9), WR (0, 9)); /* { dg-warning "writing up to 9 bytes" } */ + T ( 0, "%.*ls", IR ( 0, 53), WR (0, 9)); /* { dg-warning "writing up to 53 bytes" } */ + T ( 0, "%.*ls", IR ( 0, 55), WR (0, 9)); /* { dg-warning "writing up to 54 bytes" } */ + T ( 0, "%.*ls", IR ( 1, 2), WR (0, 9)); /* { dg-warning "writing up to 2 bytes" } */ + T ( 0, "%.*ls", IR ( 2, 3), WR (0, 9)); /* { dg-warning "writing up to 3 bytes" } */ + T ( 0, "%.*ls", IR ( 7, 13), WR (0, 9)); /* { dg-warning "writing up to 13 bytes" } */ + + /* String between N and unknown number of characters long. */ + T ( 0, "%.*ls", IR (imin, -1), WR (0, x)); /* { dg-warning "writing a terminating nul" } */ + T ( 0, "%.*ls", IR (imin, -1), WR (1, x)); /* { dg-warning "writing likely 2 or more bytes" } */ + T ( 1, "%.*ls", IR (imin, -1), WR (1, x)); /* { dg-warning "writing likely 2 or more bytes" } */ + T ( 1, "%.*ls", IR (imin, -1), WR (8, x)); /* { dg-warning "writing likely 16 or more bytes" } */ + T ( 1, "%.*ls", IR (imin, -1), WR (x, 9)); /* { dg-warning "writing likely 18 or more bytes" } */ + + /* Unknown strings. */ + T ( 1, "%.*ls", IR (imin, -1), WR (x, y)); + T ( 1, "%.*ls", IR (imin, 0), WR (x, y)); + T ( 1, "%.*ls", IR ( -99, 1), WR (x, y)); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*ls", IR ( -2, 2), WR (x, y)); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*ls", IR ( -1, 99), WR (x, y)); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*ls", IR ( 0, 99), WR (x, y)); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*ls", IR ( 1, 99), WR (x, y)); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*ls", IR ( 9, 99), WR (x, y)); /* { dg-warning "may write a terminating nul" } */ +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79275.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79275.c new file mode 100644 index 00000000000..d3145245bfb --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr79275.c @@ -0,0 +1,14 @@ +/* PR middle-end/79275 - -Wformat-overflow false positive exceeding INT_MAX + in glibc sysdeps/posix/tempname.c + { dg-do compile } + { dg-options "-O2 -Wall -Wformat-overflow=1 -ftrack-macro-expansion=0" } */ + +typedef __SIZE_TYPE__ size_t; + +void f (char *dst, size_t n, const char *s) +{ + if (n < 2 || __INT_MAX__ - 2 < n) + n = 2; + + __builtin_sprintf (dst, "%.*s %.*s", (int)n, s, (int)n, s); /* { dg-bogus "INT_MAX" } */ +} -- 2.30.2