PR tree-optimization/87096 - Optimised snprintf is not POSIX conformant

gcc/ChangeLog:

	PR rtl-optimization/87096
	* gimple-ssa-sprintf.c (sprintf_dom_walker::handle_gimple_call): Avoid
	folding calls whose bound may exceed INT_MAX.  Diagnose bound ranges
	that exceed the limit.

gcc/testsuite/ChangeLog:

	PR tree-optimization/87096
	* gcc.dg/tree-ssa/builtin-snprintf-4.c: New test.

From-SVN: r267157
This commit is contained in:
Martin Sebor 2018-12-14 22:38:08 +00:00 committed by Martin Sebor
parent 92863013e5
commit 3e6837c2a5
4 changed files with 205 additions and 7 deletions

View File

@ -1,3 +1,10 @@
2018-12-14 Martin Sebor <msebor@redhat.com>
PR rtl-optimization/87096
* gimple-ssa-sprintf.c (sprintf_dom_walker::handle_gimple_call): Avoid
folding calls whose bound may exceed INT_MAX. Diagnose bound ranges
that exceed the limit.
2018-12-14 Martin Sebor <msebor@redhat.com>
PR web/79738

View File

@ -3990,6 +3990,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
/* True when the destination size is constant as opposed to the lower
or upper bound of a range. */
bool dstsize_cst_p = true;
bool posunder4k = true;
if (idx_dstsize == UINT_MAX)
{
@ -4022,11 +4023,20 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
"specified bound %wu exceeds maximum object size "
"%wu",
dstsize, target_size_max () / 2);
/* POSIX requires snprintf to fail if DSTSIZE is greater
than INT_MAX. Even though not all POSIX implementations
conform to the requirement, avoid folding in this case. */
posunder4k = false;
}
else if (dstsize > target_int_max ())
warning_at (gimple_location (info.callstmt), info.warnopt (),
"specified bound %wu exceeds %<INT_MAX%>",
dstsize);
{
warning_at (gimple_location (info.callstmt), info.warnopt (),
"specified bound %wu exceeds %<INT_MAX%>",
dstsize);
/* POSIX requires snprintf to fail if DSTSIZE is greater
than INT_MAX. Avoid folding in that case. */
posunder4k = false;
}
}
else if (TREE_CODE (size) == SSA_NAME)
{
@ -4035,9 +4045,29 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
of them at level 2. */
value_range *vr = evrp_range_analyzer.get_value_range (size);
if (range_int_cst_p (vr))
dstsize = (warn_level < 2
? TREE_INT_CST_LOW (vr->max ())
: TREE_INT_CST_LOW (vr->min ()));
{
unsigned HOST_WIDE_INT minsize = TREE_INT_CST_LOW (vr->min ());
unsigned HOST_WIDE_INT maxsize = TREE_INT_CST_LOW (vr->max ());
dstsize = warn_level < 2 ? maxsize : minsize;
if (minsize > target_int_max ())
warning_at (gimple_location (info.callstmt), info.warnopt (),
"specified bound range [%wu, %wu] exceeds "
"%<INT_MAX%>",
minsize, maxsize);
/* POSIX requires snprintf to fail if DSTSIZE is greater
than INT_MAX. Avoid folding if that's possible. */
if (maxsize > target_int_max ())
posunder4k = false;
}
else if (vr->varying_p ())
{
/* POSIX requires snprintf to fail if DSTSIZE is greater
than INT_MAX. Since SIZE's range is unknown, avoid
folding. */
posunder4k = false;
}
/* The destination size is not constant. If the function is
bounded (e.g., snprintf) a lower bound of zero doesn't
@ -4122,7 +4152,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
directive. Clear POSUNDER4K for the former set of functions and set
it to true for the latter (it can only be cleared later, but it is
never set to true again). */
res.posunder4k = dstptr;
res.posunder4k = posunder4k && dstptr;
bool success = compute_format_length (info, &res);
if (res.warned)

View File

@ -1,3 +1,8 @@
2018-12-14 Martin Sebor <msebor@redhat.com>
PR tree-optimization/87096
* gcc.dg/tree-ssa/builtin-snprintf-4.c: New test.
2018-12-14 Alexandre Oliva <aoliva@redhat.com>
PR c++/87814

View File

@ -0,0 +1,156 @@
/* PR tree-optimization/87096 - "Optimised" snprintf is not POSIX conformant
Verify that calls to snprintf with size in excess of INT_MAX are not
treated as successful.
It would be valid for GCC to fold some of these calls to a negative
value provided it also arranged to set errno to EOVERFLOW. If that
is ever implemented this test will need to be adjusted.
{ dg-do compile }
{ dg-options "-O2 -Wall -fdump-tree-optimized -ftrack-macro-expansion=0" } */
#include "../range.h"
typedef __builtin_va_list va_list;
extern int snprintf (char*, size_t, const char*, ...);
extern int vsnprintf (char*, size_t, const char*, va_list);
#define CAT(x, y) x ## y
#define CONCAT(x, y) CAT (x, y)
#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
#define FAIL(name) do { \
extern void FAILNAME (name) (void); \
FAILNAME (name)(); \
} while (0)
/* Macro to emit a call to function named
call_in_true_branch_not_eliminated_on_line_NNN()
for each expression that's expected to fold to false but that
GCC does not fold. The dg-final scan-tree-dump-time directive
at the bottom of the test verifies that no such call appears
in output. */
#define ELIM(expr) \
if ((expr)) FAIL (in_true_branch_not_eliminated); else (void)0
/* Macro to emit a call to a function named
call_made_in_{true,false}_branch_on_line_NNN()
for each call that's expected to be retained. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that the expected number of both kinds of calls appears in output
(a pair for each line with the invocation of the KEEP() macro. */
#define KEEP(expr) \
if (expr) \
FAIL (made_in_true_branch); \
else \
FAIL (made_in_false_branch)
extern void sink (int, ...);
#define sink(...) sink (0, __VA_ARGS__)
#define WARN(N, expr) \
do { \
char a[N]; \
expr; \
sink (a); \
} while (0)
static const size_t imax = __INT_MAX__;
static const size_t imaxp1 = imax + 1;
static const size_t dmax = __PTRDIFF_MAX__;
static const size_t dmaxp1 = dmax + 1;
static const size_t szmax = __SIZE_MAX__;
static const size_t szmaxm1 = __SIZE_MAX__ - 1;
void test_size_cst (char **d)
{
ELIM (0 > snprintf (*d++, imax, "%s", ""));
KEEP (0 > snprintf (*d++, imaxp1, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
KEEP (0 > snprintf (*d++, dmax, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
KEEP (0 > snprintf (*d++, dmaxp1, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
KEEP (0 > snprintf (*d++, szmaxm1, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
KEEP (0 > snprintf (*d++, szmax, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
}
void test_size_cst_va (char **d, va_list va)
{
ELIM (0 > vsnprintf (*d++, imax, " ", va));
KEEP (0 > vsnprintf (*d++, imaxp1, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
KEEP (0 > vsnprintf (*d++, dmax, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
KEEP (0 > vsnprintf (*d++, dmaxp1, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
KEEP (0 > vsnprintf (*d++, szmaxm1, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
KEEP (0 > vsnprintf (*d++, szmax, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
}
void test_size_range (char **d)
{
size_t r = UR (imax - 1, imax);
ELIM (0 > snprintf (*d++, r, "%s", ""));
r = UR (imax, imax + 1);
KEEP (0 > snprintf (*d++, r, "%s", ""));
r = UR (imaxp1, imaxp1 + 1);
KEEP (0 > snprintf (*d++, r, "%s", "")); /* { dg-warning "specified bound range \\\[\[0-9\]+, \[0-9\]+] exceeds .INT_MAX." } */
r = UR (dmax, dmaxp1);
KEEP (0 > snprintf (*d++, r, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
r = UR (dmaxp1, dmaxp1 + 1);
KEEP (0 > snprintf (*d++, r, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
r = UR (szmaxm1, szmax);
KEEP (0 > snprintf (*d++, r, "%s", "")); /* { dg-warning "\\\[-Wformat-truncation=]" } */
}
void test_size_range_va (char **d, va_list va)
{
size_t r = UR (imax - 1, imax);
ELIM (0 > vsnprintf (*d++, r, " ", va));
r = UR (imax, imax + 1);
KEEP (0 > vsnprintf (*d++, r, " ", va));
r = UR (imaxp1, imaxp1 + 1);
KEEP (0 > vsnprintf (*d++, r, " ", va)); /* { dg-warning "specified bound range \\\[\[0-9\]+, \[0-9\]+] exceeds .INT_MAX." } */
r = UR (dmax, dmaxp1);
KEEP (0 > vsnprintf (*d++, r, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
r = UR (dmaxp1, dmaxp1 + 1);
KEEP (0 > vsnprintf (*d++, r, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
r = UR (szmaxm1, szmax);
KEEP (0 > vsnprintf (*d++, r, " ", va)); /* { dg-warning "\\\[-Wformat-truncation=]" } */
}
void test_size_varying (char **d, size_t n)
{
KEEP (0 > snprintf (*d++, n, "%s", ""));
n += 1;
KEEP (0 > snprintf (*d++, n, "%s", ""));
}
void test_size_varying_va (char **d, size_t n, va_list va)
{
KEEP (0 > vsnprintf (*d++, n, " ", va));
n += 1;
KEEP (0 > vsnprintf (*d++, n, " ", va));
}
/* { dg-final { scan-tree-dump-times " = snprintf" 12 "optimized"} }
{ dg-final { scan-tree-dump-times " = vsnprintf" 12 "optimized"} } */