* [PATCH] correct handling of negative-positive precision ranges with floating directives (PR 79800)
@ 2017-03-14 0:33 Martin Sebor
2017-03-15 4:31 ` Jeff Law
0 siblings, 1 reply; 2+ messages in thread
From: Martin Sebor @ 2017-03-14 0:33 UTC (permalink / raw)
To: Gcc Patch List
[-- Attachment #1: Type: text/plain, Size: 1000 bytes --]
The output of a floating point directive whose precision is
specified by an asterisk with an argument that's in a range
that includes both negative and positive values less than
six may include between zero and six fractional digits plus
a decimal point. For example,
printf ("%.*e", p, x);
results in the 14 bytes
-1.797693e+308
when p == -1 and x == -DBL_MIN because a negative precision
is ignored and the directive assumes the default 6, and in
just the one byte
0
when p == 0 and x == 0.0.
Current trunk doesn't handle this case correctly when the
floating argument isn't known and uses the upper bound of
the specified precision as the maximum number of fractional
digits. As a result, it sets the range on the return value
in this case as [5, 7] (plus 5 for the longest multibyte
decimal decimal point) when the correct range is [5, 14] as
explained above (plus 5 again).
The attached patch corrects the handling of such precision
ranges to avoid this unlikely bug.
Martin
[-- Attachment #2: gcc-79800.diff --]
[-- Type: text/x-patch, Size: 9091 bytes --]
PR tree-optimization/79800 - wrong snprintf result range with precision in a narrow negative-positive range
gcc/ChangeLog:
PR tree-optimization/79800
* gimple-ssa-sprintf.c (format_floating: Add argument. Handle
precision in negative-positive range.
(format_floating): Call non-const overload with adjusted precision.
gcc/testsuite/ChangeLog:
PR tree-optimization/79800
* gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: Add test cases.
* gcc.dg/tree-ssa/pr79800.c: New test.
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index 0448b21..2474391 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -1499,11 +1499,13 @@ format_floating_max (tree type, char spec, HOST_WIDE_INT prec)
}
/* Return a range representing the minimum and maximum number of bytes
- that the directive DIR will output for any argument. This function
- is used when the directive argument or its value isn't known. */
+ that the directive DIR will output for any argument. PREC gives
+ the adjusted precision range to account for negative precisions
+ meaning the default 6. This function is used when the directive
+ argument or its value isn't known. */
static fmtresult
-format_floating (const directive &dir)
+format_floating (const directive &dir, const HOST_WIDE_INT prec[2])
{
tree type;
@@ -1532,8 +1534,8 @@ format_floating (const directive &dir)
/* The minimum output as determined by flags. It's always at least 1.
When plus or space are set the output is preceded by either a sign
or a space. */
- int flagmin = (1 /* for the first digit */
- + (dir.get_flag ('+') | dir.get_flag (' ')));
+ unsigned flagmin = (1 /* for the first digit */
+ + (dir.get_flag ('+') | dir.get_flag (' ')));
/* When the pound flag is set the decimal point is included in output
regardless of precision. Whether or not a decimal point is included
@@ -1557,14 +1559,13 @@ format_floating (const directive &dir)
+ minprec
+ 3 /* p+0 */);
- res.range.max = format_floating_max (type, 'a', dir.prec[1]);
+ res.range.max = format_floating_max (type, 'a', prec[1]);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
res.range.unlikely = res.range.max;
- if (dir.prec[0] != dir.prec[1]
- || dir.prec[0] == -1 || dir.prec[0] > 0)
+ if (dir.prec[1] > 0)
res.range.unlikely += target_mb_len_max () - 1;
break;
@@ -1573,23 +1574,18 @@ format_floating (const directive &dir)
case 'E':
case 'e':
{
+ /* Minimum output attributable to precision and, when it's
+ non-zero, decimal point. */
+ HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0;
+
/* The minimum output is "[-+]1.234567e+00" regardless
of the value of the actual argument. */
- HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
- if ((dir.prec[0] < 0 && dir.prec[1] > -1) || dir.prec[0] == 0)
- minprec = 0;
- else if (dir.prec[0] > 0)
- minprec = dir.prec[0] + !radix /* decimal point */;
-
res.range.min = (flagmin
+ radix
+ minprec
+ 2 /* e+ */ + 2);
- /* MPFR uses a precision of 16 by default for some reason.
- Set it to the C default of 6. */
- int maxprec = dir.prec[1] < 0 ? 6 : dir.prec[1];
- res.range.max = format_floating_max (type, 'e', maxprec);
+ res.range.max = format_floating_max (type, 'e', prec[1]);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
@@ -1605,21 +1601,19 @@ format_floating (const directive &dir)
case 'F':
case 'f':
{
+ /* Minimum output attributable to precision and, when it's non-zero,
+ decimal point. */
+ HOST_WIDE_INT minprec = prec[0] ? prec[0] + !radix : 0;
+
/* The lower bound when precision isn't specified is 8 bytes
("1.23456" since precision is taken to be 6). When precision
is zero, the lower bound is 1 byte (e.g., "1"). Otherwise,
when precision is greater than zero, then the lower bound
is 2 plus precision (plus flags). */
- HOST_WIDE_INT minprec = 0;
- if (dir.prec[0] < 0)
- minprec = dir.prec[1] < 0 ? 6 + !radix /* decimal point */ : 0;
- else if (dir.prec[0])
- minprec = dir.prec[0] + !radix /* decimal point */;
-
res.range.min = flagmin + radix + minprec;
/* Compute the upper bound for -TYPE_MAX. */
- res.range.max = format_floating_max (type, 'f', dir.prec[1]);
+ res.range.max = format_floating_max (type, 'f', prec[1]);
/* The minimum output with unknown precision is a single byte
(e.g., "0") but the more likely output is 3 bytes ("0.0"). */
@@ -1659,6 +1653,8 @@ format_floating (const directive &dir)
else if (maxprec < 0)
maxprec = 5;
}
+ else
+ maxprec = prec[1];
res.range.max = format_floating_max (type, spec, maxprec);
@@ -1702,9 +1698,6 @@ format_floating (const directive &dir)
static fmtresult
format_floating (const directive &dir, tree arg)
{
- if (!arg || TREE_CODE (arg) != REAL_CST)
- return format_floating (dir);
-
HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] };
/* For an indeterminate precision the lower bound must be assumed
@@ -1767,6 +1760,9 @@ format_floating (const directive &dir, tree arg)
}
}
+ if (!arg || TREE_CODE (arg) != REAL_CST)
+ return format_floating (dir, prec);
+
/* The minimum and maximum number of bytes produced by the directive. */
fmtresult res;
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c
index c38a656..0b863a8 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-15.c
@@ -113,9 +113,16 @@ void test_unknown_precision_integer (int p, int i, double d)
void test_unknown_precision_floating (int p, double d)
{
+ T ( 0, "%.*a", R (-1, 0), d); /* { dg-warning "between 6 and 24 " } */
+ T ( 6, "%.*a", R (-1, 0), d); /* { dg-warning "writing a terminating nul" } */
+ T ( 7, "%.*a", R (-1, 0), d);
T ( 7, "%.*a", p, d);
T (21, "%.*a", p, 3.141);
+ T ( 0, "%.*e", R (-1, 0), d); /* { dg-warning "between 5 and 14 " } */
+ T ( 0, "%.*e", R (-1, 6), d); /* { dg-warning "between 5 and 14 " } */
+ T ( 5, "%.*e", R (-1, 6), d); /* { dg-warning "writing a terminating nul" } */
+ T ( 6, "%.*e", R (-1, 6), d);
/* "%.0e", 0.0 results in 5 bytes: "0e+00" */
T ( 5, "%.*e", p, d); /* { dg-warning "writing a terminating nul" } */
/* "%#.0e", 0.0 results in 6 bytes: "0.e+00" */
@@ -125,6 +132,10 @@ void test_unknown_precision_floating (int p, double d)
T ( 6, "%#.*e", p, 3.141); /* { dg-warning "writing a terminating nul" } */
T ( 7, "%#.*e", p, 3.141);
+ T ( 0, "%.*f", R (-1, 0), d); /* { dg-warning "between 1 and 317 " } */
+ T ( 0, "%.*f", R (-1, 6), d); /* { dg-warning "between 1 and 317 " } */
+ T ( 3, "%.*f", R (-1, 6), d); /* { dg-warning "may write a terminating nul" } */
+ T ( 4, "%.*f", R (-1, 6), d);
/* "%.0f", 0.0 results in 1 byte: "0" but precision of at least 1
is likely, resulting in "0.0". */
T ( 3, "%.*f", p, d); /* { dg-warning "may write a terminating nul" } */
@@ -138,12 +149,16 @@ void test_unknown_precision_floating (int p, double d)
T ( 3, "%#.*f", p, 3.141); /* { dg-warning "may write a terminating nul" } */
T ( 4, "%#.*f", p, 3.141);
+ T ( 0, "%.*g", R (-1, 0), d); /* { dg-warning "between 1 and 13 " } */
+ T (12, "%.*g", R (-1, 0), d); /* { dg-warning "may write a terminating nul" } */
+ T (13, "%.*g", R (-1, 0), d);
T (12, "%.*g", p, d); /* { dg-warning "may write a terminating nul" } */
T (12, "%#.*g", p, d); /* { dg-warning "may write a terminating nul" } */
T (13, "%.*g", p, d);
T (13, "%#.*g", p, d);
- T ( 6, "%#.*g", R (-1, 0), d);/* { dg-warning "may write a terminating nul" } */
- T ( 7, "%#.*g", R (-1, 0), d);
+ T (12, "%#.*g", R (-1, 0), d);/* { dg-warning "may write a terminating nul" } */
+ T (12, "%#.*g", R (-1, 6), d);/* { dg-warning "may write a terminating nul" } */
+ T (13, "%#.*g", R (-1, 0), d);
T ( 6, "%#.*g", R ( 0, 0), d);/* { dg-warning "may write a terminating nul" } */
T ( 7, "%#.*g", R ( 0, 0), d);
T ( 6, "%#.*g", R ( 0, 1), d);/* { dg-warning "may write a terminating nul" } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr79800.c b/gcc/testsuite/gcc.dg/tree-ssa/pr79800.c
new file mode 100644
index 0000000..180a6e7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr79800.c
@@ -0,0 +1,31 @@
+/* PR 79800 - wrong snprintf result range with precision in a narrow
+ negative-positive range
+ { dg-do "run" }
+ { dg-options "-O2 -Wall" } */
+
+#define FMT "%.*a"
+char fmt[] = FMT;
+
+volatile double x = 1.23456789;
+
+void f (int p)
+{
+ if (p < -1 || 0 < p)
+ p = -1;
+
+ char d[30];
+ int n1 = __builtin_sprintf (d, "%.*a", p, x);
+ const char *s = n1 < 20 ? "< 20" : ">= 20";
+
+ if (__builtin_strcmp (s, ">= 20"))
+ __builtin_abort ();
+}
+
+volatile int i = -1;
+
+int main ()
+{
+ f (i);
+
+ return 0;
+}
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] correct handling of negative-positive precision ranges with floating directives (PR 79800)
2017-03-14 0:33 [PATCH] correct handling of negative-positive precision ranges with floating directives (PR 79800) Martin Sebor
@ 2017-03-15 4:31 ` Jeff Law
0 siblings, 0 replies; 2+ messages in thread
From: Jeff Law @ 2017-03-15 4:31 UTC (permalink / raw)
To: Martin Sebor, Gcc Patch List
On 03/13/2017 06:33 PM, Martin Sebor wrote:
> The output of a floating point directive whose precision is
> specified by an asterisk with an argument that's in a range
> that includes both negative and positive values less than
> six may include between zero and six fractional digits plus
> a decimal point. For example,
>
> printf ("%.*e", p, x);
>
> results in the 14 bytes
>
> -1.797693e+308
>
> when p == -1 and x == -DBL_MIN because a negative precision
> is ignored and the directive assumes the default 6, and in
> just the one byte
>
> 0
>
> when p == 0 and x == 0.0.
>
> Current trunk doesn't handle this case correctly when the
> floating argument isn't known and uses the upper bound of
> the specified precision as the maximum number of fractional
> digits. As a result, it sets the range on the return value
> in this case as [5, 7] (plus 5 for the longest multibyte
> decimal decimal point) when the correct range is [5, 14] as
> explained above (plus 5 again).
>
> The attached patch corrects the handling of such precision
> ranges to avoid this unlikely bug.
>
> Martin
>
> gcc-79800.diff
>
>
> PR tree-optimization/79800 - wrong snprintf result range with precision in a narrow negative-positive range
>
> gcc/ChangeLog:
>
> PR tree-optimization/79800
> * gimple-ssa-sprintf.c (format_floating: Add argument. Handle
> precision in negative-positive range.
> (format_floating): Call non-const overload with adjusted precision.
>
> gcc/testsuite/ChangeLog:
>
> PR tree-optimization/79800
> * gcc.dg/tree-ssa/builtin-sprintf-warn-15.c: Add test cases.
> * gcc.dg/tree-ssa/pr79800.c: New test.
Thanks. Installed.
jeff
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2017-03-15 4:31 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-14 0:33 [PATCH] correct handling of negative-positive precision ranges with floating directives (PR 79800) Martin Sebor
2017-03-15 4:31 ` Jeff Law
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).