From patchwork Fri Dec 2 02:31:18 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 86190 Delivered-To: patch@linaro.org Received: by 10.140.20.101 with SMTP id 92csp56044qgi; Thu, 1 Dec 2016 18:31:50 -0800 (PST) X-Received: by 10.99.251.69 with SMTP id w5mr75701349pgj.124.1480645910336; Thu, 01 Dec 2016 18:31:50 -0800 (PST) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id g5si2713497pfj.289.2016.12.01.18.31.50 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 01 Dec 2016 18:31:50 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-return-443291-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) client-ip=209.132.180.131; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org; spf=pass (google.com: domain of gcc-patches-return-443291-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=gcc-patches-return-443291-patch=linaro.org@gcc.gnu.org; dmarc=fail (p=NONE dis=NONE) header.from=gmail.com DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :subject:to:references:cc:from:message-id:date:mime-version :in-reply-to:content-type; q=dns; s=default; b=k1k+NPScPuhaJgyqQ GEsQrnMcu2DtPahdE8Xv3mnbaG+ooUeA1mdpxWPwhS0T5eqFgZSak7ywh+Vx4z0G 2GYjjKJcH7271JryWFBtdigNhnycr+P9kQHS6cni4Hpvq4gpwXMGvrls/98564n6 YhduCDW5e4FM/BJcAIuwhfTIVg= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :subject:to:references:cc:from:message-id:date:mime-version :in-reply-to:content-type; s=default; bh=m4nKeUtSPjIfWyoQHTwTFYi tTBQ=; b=Z+93t5Y25CVyvWB0p9tzCA6w1TQ+YyNpLMeyoZeK8xWsHirTAzU1ksv /4cYGhzRF+XYlXAze7mXDe/U3mqjPVqIr1FbbELRYi8rFPGSRZMkTm2CXeoeSeBZ 9NtQkaG/nMJStAQOejEMUDclmVRJdG3qf5fs0+b2yK6oJ2dzvG7Q= Received: (qmail 61477 invoked by alias); 2 Dec 2016 02:31:28 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 61455 invoked by uid 89); 2 Dec 2016 02:31:27 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.8 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=Attached, 2788 X-HELO: mail-qk0-f195.google.com Received: from mail-qk0-f195.google.com (HELO mail-qk0-f195.google.com) (209.85.220.195) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 02 Dec 2016 02:31:23 +0000 Received: by mail-qk0-f195.google.com with SMTP id n204so28795792qke.2 for ; Thu, 01 Dec 2016 18:31:23 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:subject:to:references:cc:from:message-id:date :user-agent:mime-version:in-reply-to; bh=Wu7QBK3reBjtNTNZeGwmDqXgiWjtJ6HdLwM3Q33zBp4=; b=fUsUYJl3Q2a9Sz/SSUHWBKdJAvjHOQCSQpzZhn6lNaMgvfJJZenbvLWXF036EoOKqA s2qHwCz3gXyBApgCuw8xGT5r2nFiSEBltHWuyVk6ubMdOZSlUsDpm9Y4jsbWMwo0misK 2JlMh24hw5aaoKPt42ahUFFsJcOA8nQjBjneLKTnJoEdif4jgiGmyJ7HEGO9+oTI8xxX xO8a/McEV8pzEBoYmBk6cDyrC9dnzWxb2ypjSk+zc9dx3MzykDwpL0j0e/S5s37SNF9M mmo4krZB/WLhP/EGIjZ9otFg18owsbjtaHKirI6rXLBmP8c+4z7raLbA1OCRNe+3z7Lv qe3g== X-Gm-Message-State: AKaTC01X9Pr8d0CwHoqGKEa4aek4KknyWKJzcQlJPG+RALHr1wnLC7p1Pf/rJ/3kcRV3WA== X-Received: by 10.55.16.10 with SMTP id a10mr35508374qkh.66.1480645881646; Thu, 01 Dec 2016 18:31:21 -0800 (PST) Received: from [192.168.0.26] (97-122-174-22.hlrn.qwest.net. [97.122.174.22]) by smtp.gmail.com with ESMTPSA id k16sm1583519qtc.22.2016.12.01.18.31.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 01 Dec 2016 18:31:21 -0800 (PST) Subject: Re: [PATCH] handle integer overflow/wrapping in printf directives (PR 78622) To: Jakub Jelinek References: <20161201072647.GF3541@tucnak.redhat.com> <20161201091526.GH3541@tucnak.redhat.com> Cc: !Gcc Patch List From: Martin Sebor Message-ID: Date: Thu, 1 Dec 2016 19:31:18 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.3.0 MIME-Version: 1.0 In-Reply-To: <20161201091526.GH3541@tucnak.redhat.com> X-IsSubscribed: yes On 12/01/2016 02:15 AM, Jakub Jelinek wrote: > On Thu, Dec 01, 2016 at 08:26:47AM +0100, Jakub Jelinek wrote: >> Isn't this too simplistic? I mean, if you have say dirtype of signed char >> and argmin say 4096 + 32 and argmax say 4096 + 64, (signed char) arg >> has range 32, 64, while I think your routine will yield -128, 127 (well, >> 0 as min and -128 as max as that is what you return for signed type). >> >> Can't you subtract argmax - argmin (best just in wide_int, no need to build >> trees), and use what you have just for the case where that number doesn't >> fit into the narrower precision, otherwise if argmin - (dirtype) argmin >> == argmax - (dirtype) argmax, just use (dirtype) argmin and (dirtype) argmax >> as the range, and in case that it crosses a boundary figure if you can do >> anything than the above? Guess all cases of signed/unsigned dirtype and/or >> argtype need to be considered. > > Richard noted that you could have a look at CONVERT_EXPR_CODE_P > handling in extract_range_from_unary_expr. I think it is the > || (vr0.type == VR_RANGE > && integer_zerop (int_const_binop (RSHIFT_EXPR, > int_const_binop (MINUS_EXPR, vr0.max, vr0.min), > size_int (TYPE_PRECISION (outer_type))))))) > part that is important here for the narrowing conversion. Thanks, that was a helpful suggestion! Attached is an update that follows the vrp approach. I assume the infinity stuff is specific to the VRP pass and not something I need to worry about here. Martin PS To your earlier question, argmin and argmax have the same meaning in the added helper function as in its caller. PR middle-end/78622 - [7 Regression] -Wformat-length/-fprintf-return-value incorrect with overflow/wrapping gcc/ChangeLog: PR middle-end/78622 * gimple-ssa-sprintf.c (min_bytes_remaining): Use res.knownrange rather than res.bounded. (adjust_range_for_overflow): New function. (format_integer): Always set res.bounded to true unless either precision or width is specified and unknown. Call adjust_range_for_overflow. (format_directive): Remove vestigial quoting. (add_bytes): Correct the computation of boundrange used to decide whether a warning is of a "maybe" or "defnitely" kind. gcc/testsuite/ChangeLog: PR middle-end/78622 * gcc.c-torture/execute/pr78622.c: New test. * gcc.dg/tree-ssa/builtin-sprintf-2.c: Remove "benign" undefined behavior inadvertently introduced in a previous commit. Tighten up final checking. * gcc.dg/tree-ssa/builtin-sprintf-5.c: Rename macros for clarity. Add test cases. * gcc.dg/tree-ssa/builtin-sprintf-6.c: Add test cases. * gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Same. * gcc.dg/tree-ssa/builtin-sprintf-warn-6.c: Remove xfails and add a final optimization check. * gcc.dg/tree-ssa/builtin-sprintf.c: Add test cases. diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c index 99a635a..95a8710 100644 --- a/gcc/gimple-ssa-sprintf.c +++ b/gcc/gimple-ssa-sprintf.c @@ -637,7 +637,7 @@ min_bytes_remaining (unsigned HOST_WIDE_INT navail, const format_result &res) if (HOST_WIDE_INT_MAX <= navail) return navail; - if (1 < warn_format_length || res.bounded) + if (1 < warn_format_length || res.knownrange) { /* At level 2, or when all directives output an exact number of bytes or when their arguments were bounded by known @@ -795,6 +795,59 @@ get_width_and_precision (const conversion_spec &spec, *pprec = prec; } +/* With the range [*ARGMIN, *ARGMAX] of an integer directive's actual + argument, due to the conversion from either *ARGMIN or *ARGMAX to + the type of the directive's formal argument it's possible for both + to result in the same number of bytes or a range of bytes that's + less than the number of bytes that would result from formatting + some other value in the range [*ARGMIN, *ARGMAX]. This can be + determined by checking for the actual argument being in the range + of the type of the directive. If it isn't it must be assumed to + take on the full range of the directive's type. + Return true when the range has been adjusted to the range of + DIRTYPE, false otherwise. */ + +static bool +adjust_range_for_overflow (tree dirtype, tree *argmin, tree *argmax) +{ + unsigned argprec = TYPE_PRECISION (TREE_TYPE (*argmin)); + unsigned dirprec = TYPE_PRECISION (dirtype); + + /* The logic here was inspired/lifted from the CONVERT_EXPR_CODE_P + branch in the extract_range_from_unary_expr function in tree-vrp.c. + */ + + if (TREE_CODE (*argmin) == INTEGER_CST + && TREE_CODE (*argmax) == INTEGER_CST + && (dirprec >= argprec + || integer_zerop (int_const_binop (RSHIFT_EXPR, + int_const_binop (MINUS_EXPR, + *argmax, + *argmin), + size_int (dirprec))))) + { + *argmin = force_fit_type (dirtype, wi::to_widest (*argmin), 0, false); + *argmax = force_fit_type (dirtype, wi::to_widest (*argmax), 0, false); + return false; + } + + tree dirmin = TYPE_MIN_VALUE (dirtype); + tree dirmax = TYPE_MAX_VALUE (dirtype); + + if (TYPE_UNSIGNED (dirtype)) + { + *argmin = dirmin; + *argmax = dirmax; + } + else + { + *argmin = integer_zero_node; + *argmax = dirmin; + } + + return true; +} + /* Return a range representing the minimum and maximum number of bytes that the conversion specification SPEC will write on output for the integer argument ARG when non-null. ARG may be null (for vararg @@ -989,6 +1042,10 @@ format_integer (const conversion_spec &spec, tree arg) fmtresult res; + /* The result is bounded unless width or precision has been specified + whose value is unknown. */ + res.bounded = width != HOST_WIDE_INT_MIN && prec != HOST_WIDE_INT_MIN; + /* Using either the range the non-constant argument is in, or its type (either "formal" or actual), create a range of values that constrain the length of output given the warning level. */ @@ -1010,6 +1067,12 @@ format_integer (const conversion_spec &spec, tree arg) res.argmax = build_int_cst (argtype, wi::fits_uhwi_p (max) ? max.to_uhwi () : max.to_shwi ()); + /* Set KNOWNRANGE if the argument is in a known subrange + of the directive's type (KNOWNRANGE may be reset below). */ + res.knownrange + = (!tree_int_cst_equal (TYPE_MIN_VALUE (dirtype), res.argmin) + && !tree_int_cst_equal (TYPE_MAX_VALUE (dirtype), res.argmax)); + /* For a range with a negative lower bound and a non-negative upper bound, use one to determine the minimum number of bytes on output and whichever of the two bounds that results in @@ -1020,6 +1083,10 @@ format_integer (const conversion_spec &spec, tree arg) { argmin = res.argmin; argmax = res.argmax; + + res.knownrange + &= !adjust_range_for_overflow (dirtype, &argmin, &argmax); + int minbytes = format_integer (spec, res.argmin).range.min; int maxbytes = format_integer (spec, res.argmax).range.max; if (maxbytes < minbytes) @@ -1032,11 +1099,6 @@ format_integer (const conversion_spec &spec, tree arg) argmin = res.argmin; argmax = res.argmax; } - - /* The argument is bounded by the known range of values - determined by Value Range Propagation. */ - res.bounded = true; - res.knownrange = true; } else if (range_type == VR_ANTI_RANGE) { @@ -1101,6 +1163,10 @@ format_integer (const conversion_spec &spec, tree arg) res.argmax = argmax; } + /* Clear KNOWNRANGE if the range has been adjusted to the maximum + of the directive. */ + res.knownrange &= !adjust_range_for_overflow (dirtype, &argmin, &argmax); + /* Recursively compute the minimum and maximum from the known range, taking care to swap them if the lower bound results in longer output than the upper bound (e.g., in the range [-1, 0]. */ @@ -1879,7 +1945,7 @@ format_directive (const pass_sprintf_length::call_info &info, fmtres.argmin, fmtres.argmax); else inform (info.fmtloc, - "using the range [%qE, %qE] for directive argument", + "using the range [%E, %E] for directive argument", fmtres.argmin, fmtres.argmax); } } @@ -2057,7 +2123,7 @@ add_bytes (const pass_sprintf_length::call_info &info, indicate that the overflow/truncation may (but need not) happen. */ bool boundrange = (res->number_chars_min < res->number_chars_max - && res->number_chars_min < info.objsize); + && res->number_chars_min + nbytes <= info.objsize); if (!end && ((nbytes - navail) == 1 || boundrange)) { diff --git a/gcc/testsuite/gcc.c-torture/execute/pr78622.c b/gcc/testsuite/gcc.c-torture/execute/pr78622.c new file mode 100644 index 0000000..44c9b0b --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/pr78622.c @@ -0,0 +1,39 @@ +/* PR middle-end/78622 - [7 Regression] -Wformat-length/-fprintf-return-value + incorrect with overflow/wrapping */ +/* { dg-additional-options "-Wformat-length=2" } */ + +__attribute__((noinline, noclone)) int +foo (int x) +{ + if (x < 4096 + 8 || x >= 4096 + 256 + 8) + return -1; + char buf[5]; + return __builtin_sprintf (buf, "%hhd", x + 1); +} + +int +bar (unsigned int x) +{ + if (x < 64 || x > 2U * __INT_MAX__ - 10) + return -1; + char buf[1]; + return __builtin_sprintf (buf, "%d", x + 1); /* { dg-warn "directive writing between 1 and 11 bytes into a region of size 1" "int32plus" { target { int32plus } } */ +} + +int +main () +{ + if (__SCHAR_MAX__ != 127 || __CHAR_BIT__ != 8 || __SIZEOF_INT__ != 4) + return 0; + if (foo (4095 + 9) != 1 + || foo (4095 + 32) != 2 + || foo (4095 + 127) != 3 + || foo (4095 + 128) != 4 + || foo (4095 + 240) != 3 + || foo (4095 + 248) != 2 + || foo (4095 + 255) != 2 + || foo (4095 + 256) != 1) + __builtin_abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-2.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-2.c index 4dddccdf..260f4fc 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-2.c @@ -25,6 +25,9 @@ char buf8k [8192]; #define concat(a, b) a ## b #define CAT(a, b) concat (a, b) +/* Calls to this function must not be eliminated. */ +void must_not_eliminate (void); + #define EQL(expect, size, fmt, ...) \ void __attribute__ ((noinline, noclone)) \ CAT (test_on_line_, __LINE__)(void) \ @@ -34,7 +37,7 @@ char buf8k [8192]; char *dst = size < 0 ? buf : buf8k + sizeof buf8k - size; \ int result = __builtin_sprintf (dst, fmt, __VA_ARGS__); \ if (result != expect) \ - __builtin_abort (); \ + must_not_eliminate (); \ } \ } @@ -50,7 +53,7 @@ char buf8k [8192]; char *dst = size < 0 ? buf : buf8k + sizeof buf8k - size; \ int result = __builtin_sprintf (dst, fmt, __VA_ARGS__); \ if (result < min || max < result) \ - __builtin_abort (); \ + must_not_eliminate (); \ } \ } @@ -75,6 +78,8 @@ EQL (0, 0, "%-s", ""); EQL (1, 1, "%c", 'x'); EQL (1, 1, "%-s", "x"); +EQL (1, 2, "%c", 'x'); + EQL (4, 4, "%4c", 'x'); /* Verify that exceeding the environmental limit of 4095 bytes for @@ -179,7 +184,7 @@ RNG (4, 4, 32, "%zu", sz) /* Exercise bug 78586. */ RNG (4, 4, 32, "%lu", (unsigned long)i) -RNG (4, 4, 32, "%lu", (unsigned)u) +RNG (4, 4, 32, "%lu", (unsigned long)u) RNG (4, 4, 32, "%lu", (unsigned long)li) RNG (4, 4, 32, "%lu", (unsigned long)lu) RNG (4, 4, 32, "%lu", (unsigned long)sz) @@ -259,21 +264,24 @@ RNG (0, 6, 8, "%s%ls", "1", L"2"); /* Verify that no call to abort has been eliminated and that each call is at the beginning of a basic block (and thus the result of a branch). This latter test tries to verify that the test preceding the call to - abort has not been eliminated either. + the must_not_eliminate() function has not been eliminated either. The expected output looks something like this: : result_3 = __builtin_sprintf (&MEM[(void *)&buf8k + 8192B], "%c", 32); if (result_3 != 0) - goto ; + goto ; [50.0%] else - goto ; + goto ; [50.0%] - : - __builtin_abort (); + [50.0%]: + must_not_eliminate (); */ -/* { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *__builtin_abort" 124 "optimized" { target { ilp32 || lp64 } } } } */ -/* { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *__builtin_abort" 93 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } } */ +/* Only conditional calls to abort should be made (with any probability): + { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 124 "optimized" { target { ilp32 || lp64 } } } } + { dg-final { scan-tree-dump-times "> \\\[\[0-9.\]+%\\\]:\n *must_not_eliminate" 93 "optimized" { target { { ! ilp32 } && { ! lp64 } } } } } + No unconditional calls to abort should be made: + { dg-final { scan-tree-dump-not ";\n *must_not_eliminate" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c index dbb0dd9..c4489ac 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-5.c @@ -8,18 +8,20 @@ #define FAIL(line) CAT (failure_on_line_, line) #define PASS(line) CAT (success_on_line_, line) -/* Emit a call to a function named failure_on_line_NNN when EXPR is false. */ -#define ASSERT(value, expect) \ +/* Emit a call to a function named failure_on_line_NNN when VALUE + is not equal to the constant EXPECTED, otherwise emit a call to + function success_on_line_NNN. */ +#define ASSERT(value, expected) \ do { \ extern void FAIL (__LINE__)(int); \ extern void PASS (__LINE__)(int); \ - if (value == expect) \ + if (value == expected) \ PASS (__LINE__)(value); \ else \ FAIL (__LINE__)(value); \ } while (0) -#define T(expect, ...) \ +#define EQL(expect, ...) \ do { \ int n = __builtin_snprintf (0, 0, __VA_ARGS__); \ ASSERT (n, expect); \ @@ -27,88 +29,118 @@ int ival (int i) { return i; } +/* Generate a signed int value in the specified range. */ + +static int +int_range (int min, int max) +{ + extern int int_value (void); + int val = int_value (); + if (val < min || max < val) + val = min; + return val; +} + +#define R(min, max) int_range (min, max) + void test_arg_int (int i, int n) { - T (1, "%i", ival (0)); - T (1, "%i", ival (1)); - T (2, "%i%i", ival (0), ival (1)); - T (3, "%i%i%i", ival (0), ival (1), ival (9)); - T (5, "%i %i %i", ival (0), ival (1), ival (9)); + EQL (1, "%i", ival (0)); + EQL (1, "%i", ival (1)); + EQL (2, "%i%i", ival (0), ival (1)); + EQL (3, "%i%i%i", ival (0), ival (1), ival (9)); + EQL (5, "%i %i %i", ival (0), ival (1), ival (9)); - T (5, "%i %i %i", ival (0), ival (1), ival (9)); + EQL (5, "%i %i %i", ival (0), ival (1), ival (9)); - T (13, "%hhu.%hhu.%hhu.%hhu", ival (23), ival (78), ival (216), ival (135)); + EQL (13, "%hhu.%hhu.%hhu.%hhu", ival (23), ival (78), ival (216), ival (135)); for (i = 0; i != 9; ++i) - T (1, "%i", i); + EQL (1, "%i", i); for (i = -n; i != n; ++i) - T (8, "%08x", i); + EQL (8, "%08x", i); /* As a special case, a precision of zero with an argument of zero results in zero bytes (unless modified by width). */ - T (0, "%.0d", ival (0)); - T (0, "%.0i", ival (0)); - T (0, "%.0o", ival (0)); - T (0, "%.0u", ival (0)); - T (0, "%.0x", ival (0)); - - T (0, "%.*d", 0, ival (0)); - T (0, "%.*i", 0, ival (0)); - T (0, "%.*o", 0, ival (0)); - T (0, "%.*u", 0, ival (0)); - T (0, "%.*x", 0, ival (0)); - - T (1, "%1.0d", ival (0)); - T (1, "%1.0i", ival (0)); - T (1, "%1.0o", ival (0)); - T (1, "%1.0u", ival (0)); - T (1, "%1.0x", ival (0)); + EQL (0, "%.0d", ival (0)); + EQL (0, "%.0i", ival (0)); + EQL (0, "%.0o", ival (0)); + EQL (0, "%.0u", ival (0)); + EQL (0, "%.0x", ival (0)); + + EQL (0, "%.*d", 0, ival (0)); + EQL (0, "%.*i", 0, ival (0)); + EQL (0, "%.*o", 0, ival (0)); + EQL (0, "%.*u", 0, ival (0)); + EQL (0, "%.*x", 0, ival (0)); + + EQL (1, "%1.0d", ival (0)); + EQL (1, "%1.0i", ival (0)); + EQL (1, "%1.0o", ival (0)); + EQL (1, "%1.0u", ival (0)); + EQL (1, "%1.0x", ival (0)); + + EQL (4, "%hhi", R (-128, -127)); + EQL (3, "%hhi", R ( -99, -10)); + EQL (2, "%hhi", R ( -9, -1)); + EQL (1, "%hhi", R ( 0, 9)); + EQL (1, "%hhi", R ( 0, 9)); + + EQL (1, "%1.0hhi", R ( 0, 1)); + EQL (1, "%1.1hhi", R ( 0, 9)); + EQL (2, "%1.2hhi", R ( 0, 9)); + EQL (3, "%1.3hhi", R ( 0, 9)); + + EQL (1, "%hhi", R (1024, 1033)); + EQL (2, "%hhi", R (1034, 1123)); + EQL (1, "%hhu", R (1024, 1033)); + EQL (2, "%hhu", R (1034, 1123)); } void test_arg_string (const char *s) { - T ( 0, "%-s", ""); - T ( 1, "%%"); - T ( 1, "%-s", "1"); - T ( 2, "%-s", "12"); - T ( 3, "%-s", "123"); - T ( 5, "s=%s", "123"); - T (10, "%%s=\"%s\"", "12345"); - - T ( 1, "%.*s", 1, "123"); - T ( 2, "%.*s", 2, "123"); - T ( 3, "%.*s", 3, "123"); - T ( 3, "%.*s", 4, "123"); - - T ( 1, "%1.*s", 1, "123"); - T ( 2, "%1.*s", 2, "123"); - T ( 3, "%1.*s", 3, "123"); - T ( 3, "%1.*s", 4, "123"); - T ( 4, "%4.*s", 1, "123"); - T ( 4, "%4.*s", 2, "123"); - T ( 4, "%4.*s", 3, "123"); - T ( 4, "%4.*s", 4, "123"); - T ( 4, "%4.*s", 5, "123"); + EQL ( 0, "%-s", ""); + EQL ( 1, "%%"); + EQL ( 1, "%-s", "1"); + EQL ( 2, "%-s", "12"); + EQL ( 3, "%-s", "123"); + EQL ( 5, "s=%s", "123"); + EQL (10, "%%s=\"%s\"", "12345"); + + EQL ( 1, "%.*s", 1, "123"); + EQL ( 2, "%.*s", 2, "123"); + EQL ( 3, "%.*s", 3, "123"); + EQL ( 3, "%.*s", 4, "123"); + + EQL ( 1, "%1.*s", 1, "123"); + EQL ( 2, "%1.*s", 2, "123"); + EQL ( 3, "%1.*s", 3, "123"); + EQL ( 3, "%1.*s", 4, "123"); + EQL ( 4, "%4.*s", 1, "123"); + EQL ( 4, "%4.*s", 2, "123"); + EQL ( 4, "%4.*s", 3, "123"); + EQL ( 4, "%4.*s", 4, "123"); + EQL ( 4, "%4.*s", 5, "123"); const char *a = "123"; const char *b = "456"; - T ( 3, "%-s", s ? a : b); - T ( 0, "%.0s", s); - T ( 1, "%1.1s", s); - T ( 2, "%2.2s", s); - T ( 2, "%2.1s", s); + EQL ( 3, "%-s", s ? a : b); + EQL ( 0, "%.0s", s); + EQL ( 1, "%1.1s", s); + EQL ( 2, "%2.2s", s); + EQL ( 2, "%2.1s", s); } void test_arg_multiarg (int i, double d) { - T (16, "%i %f %s", 123, 3.14, "abc"); - T (16, "%12i %s", i, "abc"); - T (16, "%*i %s", 12, i, "abc"); + EQL (16, "%i %f %s", 123, 3.14, "abc"); + EQL (16, "%12i %s", i, "abc"); + EQL (16, "%*i %s", 12, i, "abc"); } -#define TV(expect, fmt, va) \ +#define EQLv(expect, fmt, va) \ do { \ int n = __builtin_vsnprintf (0, 0, fmt, va); \ ASSERT (n, expect); \ @@ -116,21 +148,21 @@ void test_arg_multiarg (int i, double d) void test_va_int (__builtin_va_list va) { - TV ( 2, "%02hhx", va); - TV ( 2, "%02.*hhx", va); - TV ( 4, "%04hx", va); - TV ( 4, "%04.*hx", va); + EQLv ( 2, "%02hhx", va); + EQLv ( 2, "%02.*hhx", va); + EQLv ( 4, "%04hx", va); + EQLv ( 4, "%04.*hx", va); } void test_va_multiarg (__builtin_va_list va) { - TV ( 8, "%8x", va); - TV ( 8, "% 8x", va); - TV ( 9, "%9x", va); - TV (11, "%11o", va); - TV (12, "%12o", va); + EQLv ( 8, "%8x", va); + EQLv ( 8, "% 8x", va); + EQLv ( 9, "%9x", va); + EQLv (11, "%11o", va); + EQLv (12, "%12o", va); - TV (16, "%12i %3.2s", va); + EQLv (16, "%12i %3.2s", va); } diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-6.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-6.c index 4c41234..abd49df 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-6.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-6.c @@ -8,6 +8,8 @@ { dg-options "-O2 -Wformat -fdump-tree-optimized" } { dg-require-effective-target int32plus } */ +typedef __SIZE_TYPE__ size_t; + #define CONCAT(a, b) a ## b #define CAT(a, b) CONCAT (a, b) @@ -50,6 +52,19 @@ void test_arg_int (int width, int prec, int i, int n) T ("%i", R (1, 10)); + /* Each of the bounds of the ranges below results in just one byte + on output because they convert to the value 9 in type char, yet + other values in those ranges can result in up to four bytes. + For example, 4240 converts to -112. Verify that the output + isn't folded into a constant. This assumes __CHAR_BIT__ of 8. */ + T ("%hhi", R (4104, 4360) + 1); + T ("%hhu", R (4104, 4360) + 1); + + /* Here, the range includes all possible lengths of output for + a 16-bit short and 32-bit int. */ + T ("%hi", R (65536, 65536 * 2)); + T ("%hu", R (65536, 65536 * 2)); + T ("%'i", 1234567); for (i = -n; i != n; ++i) @@ -104,6 +119,7 @@ void test_invalid_directive (void) T ("abc%Q"); /* { dg-warning "unknown conversion type character .Q." } */ } + /* Use 'grep "^ *T (" builtin-sprintf-6.c | wc -l' to determine the count for the directive below. - { dg-final { scan-tree-dump-times "snprintf" 42 "optimized"} } */ + { dg-final { scan-tree-dump-times "snprintf" 46 "optimized"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c index fae584e..f49422a 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-1.c @@ -1039,8 +1039,8 @@ void test_sprintf_chk_s_nonconst (int w, int p, const char *s) is not. */ T ( 1, "%*s", w, ""); T ( 1, "%*s", w, "1"); /* { dg-warning "nul past the end" } */ - T ( 1, "%.*s", w, ""); - T ( 1, "%.*s", w, "1"); /* { dg-warning "may write a terminating nul" } */ + T ( 1, "%.*s", p, ""); + T ( 1, "%.*s", p, "1"); /* { dg-warning "may write a terminating nul" } */ T ( 1, "%.*s", w, "123"); /* { dg-warning "writing between 0 and 3 bytes into a region of size 1" } */ T ( 1, "%*s", w, "123"); /* { dg-warning "writing 3 or more bytes into a region of size 1" } */ @@ -1294,6 +1294,12 @@ void test_sprintf_chk_int_nonconst (int w, int p, int a) T (3, "%2x", a); T (1, "%.*d", p, a); + + T (4, "%i %i", a, a); + /* The following will definitely be "writing a terminating nul past the end" + (i.e., not "may write".) */ + T (4, "%i %i ", a, a); /* { dg-warning "writing a terminating nul past the end" } */ + T (4, "%i %i %i", a, a, a); /* { dg-warning "into a region" }*/ } void test_sprintf_chk_e_nonconst (int w, int p, double d) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-6.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-6.c index 0cb02b7..121ed4e 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-6.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-6.c @@ -2,15 +2,18 @@ Test to verify that the correct range information is made available to the -Wformat-lenght check to prevent warnings. */ /* { dg-do compile } */ -/* { dg-options "-O2 -Wformat -Wformat-length" } */ +/* { dg-options "-O2 -Wformat -Wformat-length -fdump-tree-optimized" } */ +void abort (void); int snprintf (char*, __SIZE_TYPE__, const char*, ...); void fuchar (unsigned char j, char *p) { if (j > 99) return; - snprintf (p, 4, "%3hu", j); + + if (3 != snprintf (p, 4, "%3hu", j)) + abort (); } void fschar (signed char j, char *p) @@ -20,14 +23,17 @@ void fschar (signed char j, char *p) if (k > 99) return; - snprintf (p, 3, "%3hhu", k); /* { dg-bogus "" "unsigned char" { xfail *-*-* } } */ + if (3 != snprintf (p, 4, "%3hhu", k)) + abort (); } void fushrt (unsigned short j, char *p) { if (j > 999) return; - snprintf (p, 4, "%3hu", j); + + if (3 != snprintf (p, 4, "%3hu", j)) + abort (); } void fshrt (short j, char *p) @@ -37,7 +43,8 @@ void fshrt (short j, char *p) if (k > 999) return; - snprintf (p, 4, "%3hu", k); + if (3 != snprintf (p, 4, "%3hu", k)) + abort (); } void fuint (unsigned j, char *p) @@ -54,13 +61,16 @@ void fint (int j, char *p) if (k > 999) return; - snprintf (p, 4, "%3u", k); /* { dg-bogus "" "unsigned int" { xfail *-*-* } } */ + /* Range info isn't available here. */ + snprintf (p, 4, "%3u", k); } void fulong (unsigned long j, char *p) { if (j > 999) return; + + /* Range info isn't available here. */ snprintf (p, 4, "%3lu", j); } @@ -71,13 +81,16 @@ void flong (long j, char *p) if (k > 999) return; - snprintf (p, 4, "%3lu", k); /* { dg-bogus "" "unsigned long" { xfail *-*-* } } */ + /* Range info isn't available here. */ + snprintf (p, 4, "%3lu", k); } void fullong (unsigned long long j, char *p) { if (j > 999) return; + + /* Range info isn't available here. */ snprintf (p, 4, "%3llu", j); } @@ -88,5 +101,7 @@ void fllong (long j, char *p) if (k > 999) return; - snprintf (p, 4, "%3llu", k); /* { dg-bogus "" "unsigned long long" { xfail lp64 } } */ + snprintf (p, 4, "%3llu", k); } + +/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf.c index 916df79..35a5bd0 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf.c @@ -166,27 +166,48 @@ test_c (char c) EQL (5, 6, "%c %c %c", c, c, c); } -/* Generate a pseudo-random value in the specified range. The return - value must be unsigned char to work around limitations in the GCC - range information. Similarly for the declaration of rand() whose - correct return value should be int, but that also prevents the range - information from making it to the printf pass. */ +/* Generate a pseudo-random unsigned value. */ -unsigned char uchar_range (unsigned min, unsigned max) +unsigned __attribute__ ((noclone, noinline)) +unsigned_value (void) { - extern unsigned rand (void); + extern int rand (); + return rand (); +} - unsigned x; - x = rand (); +/* Generate a pseudo-random signed value. */ - if (x < min) - x = min; - else if (max < x) - x = max; +int __attribute__ ((noclone, noinline)) +int_value (void) +{ + extern int rand (); + return rand (); +} + +/* Generate an unsigned char value in the specified range. */ +static unsigned char +uchar_range (unsigned min, unsigned max) +{ + unsigned x = unsigned_value (); + if (x < min || max < x) + x = min; return x; } +/* Generate a signed int value in the specified range. */ + +static int +int_range (int min, int max) +{ + int val = int_value (); + if (val < min || max < val) + val = min; + return val; +} + +#define IR(min, max) int_range (min, max) + static void __attribute__ ((noinline, noclone)) test_d_i (int i, long li) { @@ -266,9 +287,35 @@ test_d_i (int i, long li) RNG ( 1, 4, 5, "%hhi", i); RNG ( 1, 3, 4, "%hhu", i); + RNG ( 3, 4, 5, "%hhi", IR (-128, -10)); + RNG ( 2, 4, 5, "%hhi", IR (-128, -1)); + RNG ( 1, 4, 5, "%hhi", IR (-128, 0)); + + RNG ( 1, 4, 5, "%1hhi", IR (-128, 0)); + RNG ( 1, 4, 5, "%2hhi", IR (-128, 0)); + RNG ( 1, 4, 5, "%3hhi", IR (-128, 0)); + RNG ( 1, 4, 5, "%4hhi", IR (-128, 0)); + RNG ( 1, 5, 6, "%5hhi", IR (-128, 0)); + RNG ( 1, 6, 7, "%6hhi", IR (-128, 0)); + RNG ( 2, 6, 7, "%6hhi", IR (-128, 10)); + + RNG ( 0, 1, 2, "%.hhi", IR ( 0, 1)); + RNG ( 0, 1, 2, "%.0hhi", IR ( 0, 1)); + RNG ( 0, 1, 2, "%0.0hhi", IR ( 0, 1)); /* { dg-warning ".0. flag ignored with precision" } */ + RNG ( 0, 1, 2, "%*.0hhi", 0, IR ( 0, 1)); + + RNG ( 1, 2, 3, "%hhi", IR (1024, 1034)); + RNG ( 1, 4, 5, "%hhi", IR (1024, 2048)); + RNG ( 2, 3, 4, "%hhi", IR (1034, 1151)); + + RNG ( 1, 2, 3, "%hhu", IR (1024, 1034)); + RNG ( 1, 3, 4, "%hhu", IR (1024, 2048)); + RNG ( 2, 3, 4, "%hhu", IR (1034, 1151)); + #if __SIZEOF_SHORT__ == 2 RNG ( 1, 6, 7, "%hi", i); RNG ( 1, 5, 6, "%hu", i); + #elif __SIZEOF_SHORT__ == 4 RNG ( 1, 11, 12, "%hi", i); RNG ( 1, 10, 11, "%hu", i);