mirror of git://gcc.gnu.org/git/gcc.git
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:
parent
92863013e5
commit
3e6837c2a5
|
|
@ -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>
|
2018-12-14 Martin Sebor <msebor@redhat.com>
|
||||||
|
|
||||||
PR web/79738
|
PR web/79738
|
||||||
|
|
|
||||||
|
|
@ -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
|
/* True when the destination size is constant as opposed to the lower
|
||||||
or upper bound of a range. */
|
or upper bound of a range. */
|
||||||
bool dstsize_cst_p = true;
|
bool dstsize_cst_p = true;
|
||||||
|
bool posunder4k = true;
|
||||||
|
|
||||||
if (idx_dstsize == UINT_MAX)
|
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 "
|
"specified bound %wu exceeds maximum object size "
|
||||||
"%wu",
|
"%wu",
|
||||||
dstsize, target_size_max () / 2);
|
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 ())
|
else if (dstsize > target_int_max ())
|
||||||
|
{
|
||||||
warning_at (gimple_location (info.callstmt), info.warnopt (),
|
warning_at (gimple_location (info.callstmt), info.warnopt (),
|
||||||
"specified bound %wu exceeds %<INT_MAX%>",
|
"specified bound %wu exceeds %<INT_MAX%>",
|
||||||
dstsize);
|
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)
|
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. */
|
of them at level 2. */
|
||||||
value_range *vr = evrp_range_analyzer.get_value_range (size);
|
value_range *vr = evrp_range_analyzer.get_value_range (size);
|
||||||
if (range_int_cst_p (vr))
|
if (range_int_cst_p (vr))
|
||||||
dstsize = (warn_level < 2
|
{
|
||||||
? TREE_INT_CST_LOW (vr->max ())
|
unsigned HOST_WIDE_INT minsize = TREE_INT_CST_LOW (vr->min ());
|
||||||
: 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
|
/* The destination size is not constant. If the function is
|
||||||
bounded (e.g., snprintf) a lower bound of zero doesn't
|
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
|
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
|
it to true for the latter (it can only be cleared later, but it is
|
||||||
never set to true again). */
|
never set to true again). */
|
||||||
res.posunder4k = dstptr;
|
res.posunder4k = posunder4k && dstptr;
|
||||||
|
|
||||||
bool success = compute_format_length (info, &res);
|
bool success = compute_format_length (info, &res);
|
||||||
if (res.warned)
|
if (res.warned)
|
||||||
|
|
|
||||||
|
|
@ -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>
|
2018-12-14 Alexandre Oliva <aoliva@redhat.com>
|
||||||
|
|
||||||
PR c++/87814
|
PR c++/87814
|
||||||
|
|
|
||||||
|
|
@ -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"} } */
|
||||||
Loading…
Reference in New Issue