From patchwork Wed Dec 14 04:07:47 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sebor X-Patchwork-Id: 87978 Delivered-To: patch@linaro.org Received: by 10.140.20.101 with SMTP id 92csp1701qgi; Tue, 13 Dec 2016 20:08:52 -0800 (PST) X-Received: by 10.84.164.162 with SMTP id w31mr204793229pla.9.1481688532037; Tue, 13 Dec 2016 20:08:52 -0800 (PST) Return-Path: Received: from sourceware.org (server1.sourceware.org. [209.132.180.131]) by mx.google.com with ESMTPS id r25si50777838pgn.297.2016.12.13.20.08.51 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 13 Dec 2016 20:08:52 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-return-444351-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-444351-patch=linaro.org@gcc.gnu.org designates 209.132.180.131 as permitted sender) smtp.mailfrom=gcc-patches-return-444351-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:to :from:subject:message-id:date:mime-version:content-type; q=dns; s=default; b=n068WQXD0XKi7GhGKrRIoH3dLONipLQfEs1B0meqR0W8hLFa/4 zGUFmAfasLITI/ng8Xd5RSrSdlp+42NU432Itz530WSoHxSi/AKLqB9YLknGCKnk ENNuTyCCuQHMykI23r8VOIHWFKWUmRsrRX5ed+VpR1QLl4SHhsoHEg6YA= 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:to :from:subject:message-id:date:mime-version:content-type; s= default; bh=HYl0Nb5RqcMwLDpRCdPkt0OFvp4=; b=icPMadOFz8//gC3z+9nv L2uLVdMCmaHs//SkPjzTgz0er2YMMnSdDzcIT8rsFF3bcVMKEmXc3jsADene0ojo As9AZ64bGHel4jDFFZNbKvZ1ZvWL15oyasdd0HMQRIgGF0OL6irxTKjcRqpCPX3U dUPwXomy01LQBWAdScBS09I= Received: (qmail 33789 invoked by alias); 14 Dec 2016 04:08:10 -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 29343 invoked by uid 89); 14 Dec 2016 04:07:56 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.3 required=5.0 tests=AWL, BAYES_00, FREEMAIL_FROM, KAM_ASCII_DIVIDERS, RCVD_IN_DNSWL_LOW, RCVD_IN_SORBS_SPAM, SPF_PASS autolearn=no version=3.3.2 spammy=longest, sk:host_wi, dictated, sk:HOST_WI X-HELO: mail-qt0-f182.google.com Received: from mail-qt0-f182.google.com (HELO mail-qt0-f182.google.com) (209.85.216.182) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 14 Dec 2016 04:07:52 +0000 Received: by mail-qt0-f182.google.com with SMTP id c47so6998493qtc.2 for ; Tue, 13 Dec 2016 20:07:52 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:to:from:subject:message-id:date:user-agent :mime-version; bh=b98I3/0Do3xMpXuh7smtPUZmPU/zVCEFfzyhy5SJhtU=; b=rc7VBB+6X4bA3bEq6Lg/9mhKinTKpIsojtH27QeJbRscNk813aLv+wJgZeyW4l22Rm MH6TcDtGBe0PjE25wdbaqKYMDz3odIN8rwYKzQKtD+T+LkjVPN4DIXZigXgtux8cTAnn cU9/k3ZPDRwE154u7ndVfYJKjyEZR769JoKipKN+J5pJE5t6JvQMTV+xWphUBrhusX76 TVYK30OjL/Gp4ZXeWQS7+JkvtGnhyDvfj7A1LfXO6F6d0Uos3hGkLvbmPTd1bstlHBpS UZ4uNY/ZeJaZD83rZcADLteXoPvfu+44LC+EV9RXFDHXxxqUrs+TD0jpkKAmeKfXdiAg i9CA== X-Gm-Message-State: AKaTC01dgPXtcz0KAPDJZFceJOHQB7E7UjXLvOuJFGJK4sbi4C9J8Adzi8EyjkWKZ4C78Q== X-Received: by 10.200.46.249 with SMTP id i54mr86135597qta.13.1481688470609; Tue, 13 Dec 2016 20:07:50 -0800 (PST) Received: from [192.168.0.26] (97-124-188-210.hlrn.qwest.net. [97.124.188.210]) by smtp.gmail.com with ESMTPSA id f66sm30741153qkj.23.2016.12.13.20.07.49 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 13 Dec 2016 20:07:49 -0800 (PST) To: Gcc Patch List From: Martin Sebor Subject: [PATCH] work around MPFR undefined behavior (PR 78786) Message-ID: Date: Tue, 13 Dec 2016 21:07:47 -0700 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.3.0 MIME-Version: 1.0 X-IsSubscribed: yes In a discussion of my patch for bug 78696 I mentioned I had found a bug/limitation in MPFR that causes GCC to allocate excessive amounts of memory on some corner cases (very large precision). https://gcc.gnu.org/ml/gcc-patches/2016-12/msg01098.html I've since raised GCC bug 78786 for the GCC problem and discussed the MPFR issues with the library's maintainer. The attached patch tweaks GCC to avoid triggering the MPFR problem. There is some overlap with the attached patch and the one posted for bug 78608 (fix integer overflow bugs in gimple-ssa-sprintf.c) that will need to be resolved. The latter patch also fixes some more bugs in some esoteric corner cases having to do with flags that this patch doesn't attempt to handle. Martin PR middle-end/78786 - GCC hangs/out of memory calling sprintf with large precision gcc/ChangeLog: PR middle-end/78786 * gimple-ssa-sprintf.c (target_dir_max): New macro. (get_mpfr_format_length): New function. (format_integer): Use HOST_WIDE_INT instead of int. (format_floating_max): Same. (format_floating): Call get_mpfr_format_length. (format_directive): Use target_dir_max. gcc/testsuite/ChangeLog: PR middle-end/78786 * gcc.dg/tree-ssa/builtin-sprintf-warn-7.c: New test. Index: gcc/gimple-ssa-sprintf.c =================================================================== --- gcc/gimple-ssa-sprintf.c (revision 243624) +++ gcc/gimple-ssa-sprintf.c (working copy) @@ -84,6 +84,11 @@ along with GCC; see the file COPYING3. If not see to be used for optimization but it's good enough as is for warnings. */ #define target_mb_len_max 6 +/* The maximum number of bytes a single non-string directive can result + in. This is the result of printf("%.*Lf", INT_MAX, -LDBL_MAX) for + LDBL_MAX_10_EXP of 4932. */ +#define target_dir_max() (target_int_max () + 4932 + 2) + namespace { const pass_data pass_data_sprintf_length = { @@ -989,7 +994,7 @@ format_integer (const conversion_spec &spec, tree gcc_unreachable (); } - int len; + HOST_WIDE_INT len; if ((prec == HOST_WIDE_INT_MIN || prec == 0) && integer_zerop (arg)) { @@ -1214,11 +1219,73 @@ format_integer (const conversion_spec &spec, tree return res; } +/* Return the number of bytes that a format directive consisting of FLAGS, + PRECision, format SPECification, and MPFR rounding specifier RNDSPEC, + would result for argument X under ideal conditions (i.e., if PREC + weren't excessive). MPFR 3.1 allocates large amounts of memory for + values of PREC with large magnitude and can fail (see MPFR bug #21056). + This function works around those problems. */ + +static unsigned HOST_WIDE_INT +get_mpfr_format_length (mpfr_ptr x, const char *flags, HOST_WIDE_INT prec, + char spec, char rndspec) +{ + char fmtstr[40]; + + HOST_WIDE_INT len = strlen (flags); + + fmtstr[0] = '%'; + memcpy (fmtstr + 1, flags, len); + memcpy (fmtstr + 1 + len, ".*R", 3); + fmtstr[len + 4] = rndspec; + fmtstr[len + 5] = spec; + fmtstr[len + 6] = '\0'; + + /* Avoid passing negative precisions with larger magnitude to MPFR + to avoid exposing its bugs. (A negative precision is supposed + to be ignored.) */ + if (prec < 0) + prec = -1; + + HOST_WIDE_INT p = prec; + + if (TOUPPER (spec) == 'G') + { + /* For G/g, precision gives the maximum number of significant + digits which is bounded by LDBL_MAX_10_EXP, or, for a 128 + bit IEEE extended precision, 4932. Using twice as much + here should be more than sufficient for any real format. */ + if (9864 < prec) + prec = 9864; + p = prec; + } + else + { + /* Cap precision arbitrarily at 1KB and add the difference + (if any) to the MPFR result. */ + if (1024 < prec) + p = 1024; + } + + len = mpfr_snprintf (NULL, 0, fmtstr, (int)p, x); + + /* Handle the unlikely (impossible?) error by returning more than + the maximum dictated by the function's return type. */ + if (len < 0) + return target_dir_max () + 1; + + /* Adjust the retrun value by the difference. */ + if (p < prec) + len += prec - p; + + return len; +} + /* Return the number of bytes to format using the format specifier SPEC the largest value in the real floating TYPE. */ -static int -format_floating_max (tree type, char spec, int prec = -1) +static unsigned HOST_WIDE_INT +format_floating_max (tree type, char spec, HOST_WIDE_INT prec) { machine_mode mode = TYPE_MODE (type); @@ -1243,21 +1310,8 @@ format_integer (const conversion_spec &spec, tree mpfr_init2 (x, rfmt->p); mpfr_from_real (x, &rv, GMP_RNDN); - int n; - - if (-1 < prec) - { - const char fmt[] = { '%', '.', '*', 'R', spec, '\0' }; - n = mpfr_snprintf (NULL, 0, fmt, prec, x); - } - else - { - const char fmt[] = { '%', 'R', spec, '\0' }; - n = mpfr_snprintf (NULL, 0, fmt, x); - } - /* Return a value one greater to account for the leading minus sign. */ - return n + 1; + return 1 + get_mpfr_format_length (x, "", prec, spec, 'D'); } /* Return a range representing the minimum and maximum number of bytes @@ -1266,7 +1320,8 @@ format_integer (const conversion_spec &spec, tree is used when the directive argument or its value isn't known. */ static fmtresult -format_floating (const conversion_spec &spec, int width, int prec) +format_floating (const conversion_spec &spec, HOST_WIDE_INT width, + HOST_WIDE_INT prec) { tree type; bool ldbl = false; @@ -1357,7 +1412,7 @@ static fmtresult res.range.min = 2 + (prec < 0 ? 6 : prec); /* Compute the maximum just once. */ - const int f_max[] = { + const HOST_WIDE_INT f_max[] = { format_floating_max (double_type_node, 'f', prec), format_floating_max (long_double_type_node, 'f', prec) }; @@ -1372,10 +1427,10 @@ static fmtresult case 'g': { /* The minimum is the same as for '%F'. */ - res.range.min = 2 + (prec < 0 ? 6 : prec); + res.range.min = 1; /* Compute the maximum just once. */ - const int g_max[] = { + const HOST_WIDE_INT g_max[] = { format_floating_max (double_type_node, 'g', prec), format_floating_max (long_double_type_node, 'g', prec) }; @@ -1412,8 +1467,8 @@ format_floating (const conversion_spec &spec, tree /* Set WIDTH to -1 when it's not specified, to INT_MIN when it is specified by the asterisk to an unknown value, and otherwise to a non-negative value corresponding to the specified width. */ - int width = -1; - int prec = -1; + HOST_WIDE_INT width = -1; + HOST_WIDE_INT prec = -1; /* The minimum and maximum number of bytes produced by the directive. */ fmtresult res; @@ -1473,7 +1528,6 @@ format_floating (const conversion_spec &spec, tree char fmtstr [40]; char *pfmt = fmtstr; - *pfmt++ = '%'; /* Append flags. */ for (const char *pf = "-+ #0"; *pf; ++pf) @@ -1480,22 +1534,6 @@ format_floating (const conversion_spec &spec, tree if (spec.get_flag (*pf)) *pfmt++ = *pf; - /* Append width when specified and precision. */ - if (-1 < width) - pfmt += sprintf (pfmt, "%i", width); - if (-1 < prec) - pfmt += sprintf (pfmt, ".%i", prec); - - /* Append the MPFR 'R' floating type specifier (no length modifier - is necessary or allowed by MPFR for mpfr_t values). */ - *pfmt++ = 'R'; - - /* Save the position of the MPFR rounding specifier and skip over - it. It will be set in each iteration in the loop below. */ - char* const rndspec = pfmt++; - - /* Append the C type specifier and nul-terminate. */ - *pfmt++ = spec.specifier; *pfmt = '\0'; for (int i = 0; i != sizeof minmax / sizeof *minmax; ++i) @@ -1503,11 +1541,17 @@ format_floating (const conversion_spec &spec, tree /* Use the MPFR rounding specifier to round down in the first iteration and then up. In most but not all cases this will result in the same number of bytes. */ - *rndspec = "DU"[i]; + char rndspec = "DU"[i]; - /* Format it and store the result in the corresponding - member of the result struct. */ - *minmax[i] = mpfr_snprintf (NULL, 0, fmtstr, mpfrval); + /* Format it and store the result in the corresponding member + of the result struct. */ + unsigned HOST_WIDE_INT len + = get_mpfr_format_length (mpfrval, fmtstr, prec, + spec.specifier, rndspec); + if (0 < width && len < (unsigned)width) + len = width; + + *minmax[i] = len; } /* The range of output is known even if the result isn't bounded. */ @@ -1834,9 +1878,13 @@ format_directive (const pass_sprintf_length::call_ if (!fmtres.knownrange) { /* Only when the range is known, check it against the host value - of INT_MAX. Otherwise the range doesn't correspond to known - values of the argument. */ - if (fmtres.range.max >= target_int_max ()) + of INT_MAX + (the number of bytes of the "%.*Lf" directive with + INT_MAX precision, which is the longest possible output of any + single directive). That's the largest valid byte count (though + not valid call to a printf-like function because it can never + return such a count). Otherwise, the range doesn't correspond + to known values of the argument. */ + if (fmtres.range.max > target_dir_max ()) { /* Normalize the MAX counter to avoid having to deal with it later. The counter can be less than HOST_WIDE_INT_M1U @@ -1850,7 +1898,7 @@ format_directive (const pass_sprintf_length::call_ res->number_chars = HOST_WIDE_INT_M1U; } - if (fmtres.range.min >= target_int_max ()) + if (fmtres.range.min > target_dir_max ()) { /* Disable exact length checking after a failure to determine even the minimum number of characters (it shouldn't happen Index: gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-7.c =================================================================== --- gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-7.c (revision 0) +++ gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-7.c (working copy) @@ -0,0 +1,183 @@ +/* PR middle-end/78786 - GCC hangs/out of memory calling sprintf with large + precision + { dg-do compile } + { dg-require-effective-target int32plus } + { dg-options "-Wformat-length -ftrack-macro-expansion=0" } */ + +#define INT_MAX __INT_MAX__ +#define INT_MIN (-INT_MAX - 1) + +typedef __SIZE_TYPE__ size_t; + +void sink (int, void*); + +char buf [1]; + +#define T(n, fmt, ...) \ + sink (__builtin_sprintf (buf + sizeof buf - n, fmt, __VA_ARGS__), buf) + +void test_integer_cst (void) +{ + T (0, "%*d", INT_MIN, 0); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*d", INT_MAX, 0); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*d", INT_MIN, 0); /* { dg-warning "writing 1 byte" } */ + T (0, "%.*d", INT_MAX, 0); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%*.*d", INT_MIN, INT_MIN, 0); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*d", INT_MAX, INT_MAX, 0); /* { dg-warning "writing 2147483647 bytes" } */ +} + +void test_integer_var (int i) +{ + T (0, "%*d", INT_MIN, i); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*d", INT_MAX, i); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*d", INT_MIN, i); /* { dg-warning "writing between 1 and 11 bytes" } */ + T (0, "%.*d", INT_MAX, i); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%*.*d", INT_MIN, INT_MIN, i); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*d", INT_MAX, INT_MAX, i); /* { dg-warning "writing 2147483647 bytes" } */ +} + +void test_floating_a_cst (void) +{ + T (0, "%*a", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*a", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*a", INT_MIN, 0.); /* { dg-warning "writing 6 bytes" } */ + + T (0, "%.*a", INT_MAX, 0.); /* { dg-warning "writing 2147483654 bytes" } */ + + T (0, "%*.*a", INT_MIN, INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*a", INT_MAX, INT_MAX, 0.); /* { dg-warning "writing 2147483654 bytes" } */ +} + +void test_floating_a_var (double x) +{ + T (0, "%*a", INT_MIN, x); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*a", INT_MAX, x); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*a", INT_MIN, x); /* { dg-warning "writing between 6 and 24 bytes" } */ + + T (0, "%.*a", INT_MAX, x); /* { dg-warning "writing between 2147483653 and 2147483658 bytes" } */ + + T (0, "%*.*a", INT_MIN, INT_MIN, x); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*a", INT_MAX, INT_MAX, x); /* { dg-warning "writing between 2147483653 and 2147483658 bytes" } */ +} + +void test_floating_e_cst (void) +{ + T (0, "%*e", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*e", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*e", INT_MIN, 0.); /* { dg-warning "writing 5 bytes" } */ + + T (0, "%.*e", INT_MAX, 0.); /* { dg-warning "writing 2147483653 bytes" } */ + + T (0, "%*.*e", INT_MIN, INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*e", INT_MAX, INT_MAX, 0.); /* { dg-warning "writing 2147483653 bytes" } */ +} + +void test_floating_e_var (double x) +{ + T (0, "%*e", INT_MIN, x); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*e", INT_MAX, x); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*e", INT_MIN, x); /* { dg-warning "writing between 12 and 14 bytes" } */ + + T (0, "%.*e", INT_MAX, x); /* { dg-warning "writing between 2147483653 and 2147483655 bytes" } */ + + T (0, "%*.*e", INT_MIN, INT_MIN, x); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*e", INT_MAX, INT_MAX, x); /* { dg-warning "writing between 2147483653 and 2147483655 bytes" } */ +} + +void test_floating_f_cst (void) +{ + T (0, "%*f", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*f", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*f", INT_MIN, 0.); /* { dg-warning "writing 1 byte" } */ + + T (0, "%.*f", INT_MAX, 0.); /* { dg-warning "writing 2147483649 bytes" } */ + + T (0, "%*.*f", INT_MIN, INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*f", INT_MAX, INT_MAX, 0.); /* { dg-warning "writing 2147483649 bytes" } */ +} + +void test_floating_f_var (double x) +{ + T (0, "%*f", INT_MIN, x); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*f", INT_MAX, x); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*f", INT_MIN, x); /* { dg-warning "writing between 8 and 317 bytes" } */ + + T (0, "%.*f", INT_MAX, x); /* { dg-warning "writing between 2147483649 and 2147483958 bytes" } */ + + T (0, "%*.*f", INT_MIN, INT_MIN, x); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*f", INT_MAX, INT_MAX, x); /* { dg-warning "writing between 2147483649 and 2147483958 bytes" } */ +} + +void test_floating_g_cst (void) +{ + T (0, "%*g", INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*g", INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*g", INT_MIN, 0.); /* { dg-warning "writing 1 byte" } */ + + T (0, "%.*g", INT_MAX, 0.); /* { dg-warning "writing 1 byte" } */ + + T (0, "%*.*g", INT_MIN, INT_MIN, 0.); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*g", INT_MAX, INT_MAX, 0.); /* { dg-warning "writing 2147483647 bytes" } */ +} + +void test_floating_g (double x) +{ + T (0, "%*g", INT_MIN, x); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*g", INT_MAX, x); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*g", INT_MIN, x); /* { dg-warning "writing between 1 and 13 bytes" } */ + + T (0, "%.*g", INT_MAX, x); /* { dg-warning "writing between 1 and 310 bytes" } */ + + T (0, "%*.*g", INT_MIN, INT_MIN, x); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*g", INT_MAX, INT_MAX, x); /* { dg-warning "writing 2147483647 bytes" } */ +} + +void test_string_cst (void) +{ + T (0, "%*s", INT_MIN, ""); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*s", INT_MAX, ""); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*s", INT_MIN, ""); /* { dg-warning "writing a terminating nul" } */ + + T (0, "%.*s", INT_MAX, ""); /* { dg-warning "writing a terminating nul" } */ + + T (0, "%*.*s", INT_MIN, INT_MIN, ""); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*s", INT_MAX, INT_MAX, ""); /* { dg-warning "writing 2147483647 bytes" } */ +} + +void test_string_var (const char *s) +{ + T (0, "%*s", INT_MIN, s); /* { dg-warning "writing 2147483648 bytes" } */ + T (0, "%*s", INT_MAX, s); /* { dg-warning "writing 2147483647 bytes" } */ + + T (0, "%.*s", INT_MIN, s); /* { dg-warning "writing a terminating nul" } */ + + T (0, "%.*s", INT_MAX, s); /* { dg-warning "writing between 0 and 2147483647 bytes" } */ + + T (0, "%*.*s", INT_MIN, INT_MIN, s); /* { dg-warning "writing 2147483648 bytes" } */ + + T (0, "%*.*s", INT_MAX, INT_MAX, s); /* { dg-warning "writing 2147483647 bytes" } */ +}