Commit 4252ccd7 by Martin Sebor Committed by Martin Sebor

PR middle-end/85602 - -Wsizeof-pointer-memaccess for strncat with size of source

gcc/c-family/ChangeLog:

	PR middle-end/85602
	* c-warn.c (sizeof_pointer_memaccess_warning): Check for attribute
	nonstring.

gcc/ChangeLog:

	PR middle-end/85602
	* calls.c (maybe_warn_nonstring_arg): Handle strncat.
	* tree-ssa-strlen.c (is_strlen_related_p): Make extern.
	Handle integer subtraction.
	(maybe_diag_stxncpy_trunc): Handle nonstring source arguments.
	* tree-ssa-strlen.h (is_strlen_related_p): Declare.

gcc/testsuite/ChangeLog:

	PR middle-end/85602
	* gcc.dg/attr-nonstring-2.c: Adjust text of expected warning.
	* c-c++-common/attr-nonstring-8.c: New test.

From-SVN: r261718
parent 9e03592e
2018-06-18 Martin Sebor <msebor@redhat.com>
PR middle-end/85602
* calls.c (maybe_warn_nonstring_arg): Handle strncat.
* tree-ssa-strlen.c (is_strlen_related_p): Make extern.
Handle integer subtraction.
(maybe_diag_stxncpy_trunc): Handle nonstring source arguments.
* tree-ssa-strlen.h (is_strlen_related_p): Declare.
2018-06-18 David Malcolm <dmalcolm@redhat.com>
* config/frv/frv-protos.h (frv_ifcvt_modify_insn): Strengthen 3rd
......
2018-06-18 Martin Sebor <msebor@redhat.com>
PR middle-end/85602
* c-warn.c (sizeof_pointer_memaccess_warning): Check for attribute
nonstring.
2018-06-16 Kugan Vivekanandarajah <kuganv@linaro.org>
* c-common.c (c_common_truthvalue_conversion): Handle ABSU_EXPR.
......
......@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see
#include "gcc-rich-location.h"
#include "gimplify.h"
#include "c-family/c-indentation.h"
#include "calls.h"
/* Print a warning if a constant expression had overflow in folding.
Invoke this function on every expression that the language
......@@ -801,6 +802,12 @@ sizeof_pointer_memaccess_warning (location_t *sizeof_arg_loc, tree callee,
if (TREE_CODE (tem) == ADDR_EXPR)
tem = TREE_OPERAND (tem, 0);
/* Avoid diagnosing sizeof SRC when SRC is declared with
attribute nonstring. */
tree dummy;
if (get_attr_nonstring_decl (tem, &dummy))
return;
tree d = tree_strip_nop_conversions (dest);
if (TREE_CODE (d) == ADDR_EXPR)
d = TREE_OPERAND (d, 0);
......
......@@ -49,6 +49,7 @@ along with GCC; see the file COPYING3. If not see
#include "rtl-iter.h"
#include "tree-vrp.h"
#include "tree-ssanames.h"
#include "tree-ssa-strlen.h"
#include "intl.h"
#include "stringpool.h"
#include "attribs.h"
......@@ -1627,8 +1628,10 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
/* It's safe to call "bounded" string functions with a non-string
argument since the functions provide an explicit bound for this
purpose. */
switch (DECL_FUNCTION_CODE (fndecl))
purpose. The exception is strncat where the bound may refer to
either the destination or the source. */
int fncode = DECL_FUNCTION_CODE (fndecl);
switch (fncode)
{
case BUILT_IN_STRCMP:
case BUILT_IN_STRNCMP:
......@@ -1648,6 +1651,7 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
}
/* Fall through. */
case BUILT_IN_STRNCAT:
case BUILT_IN_STPNCPY:
case BUILT_IN_STRNCPY:
if (2 < nargs)
......@@ -1772,15 +1776,36 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
if (!decl)
continue;
tree type = TREE_TYPE (decl);
/* The maximum number of array elements accessed. */
offset_int wibnd = 0;
if (bndrng[0])
if (argno && fncode == BUILT_IN_STRNCAT)
{
/* See if the bound in strncat is derived from the length
of the strlen of the destination (as it's expected to be).
If so, reset BOUND and FNCODE to trigger a warning. */
tree dstarg = CALL_EXPR_ARG (exp, 0);
if (is_strlen_related_p (dstarg, bound))
{
/* The bound applies to the destination, not to the source,
so reset these to trigger a warning without mentioning
the bound. */
bound = NULL;
fncode = 0;
}
else if (bndrng[1])
/* Use the upper bound of the range for strncat. */
wibnd = wi::to_offset (bndrng[1]);
}
else if (bndrng[0])
/* Use the lower bound of the range for functions other than
strncat. */
wibnd = wi::to_offset (bndrng[0]);
/* Size of the array. */
/* Determine the size of the argument array if it is one. */
offset_int asize = wibnd;
bool known_size = false;
tree type = TREE_TYPE (decl);
/* Determine the array size. For arrays of unknown bound and
pointers reset BOUND to trigger the appropriate warning. */
......@@ -1789,7 +1814,10 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
if (tree arrbnd = TYPE_DOMAIN (type))
{
if ((arrbnd = TYPE_MAX_VALUE (arrbnd)))
asize = wi::to_offset (arrbnd) + 1;
{
asize = wi::to_offset (arrbnd) + 1;
known_size = true;
}
}
else if (bound == void_type_node)
bound = NULL_TREE;
......@@ -1797,13 +1825,47 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
else if (bound == void_type_node)
bound = NULL_TREE;
/* In a call to strncat with a bound in a range whose lower but
not upper bound is less than the array size, reset ASIZE to
be the same as the bound and the other variable to trigger
the apprpriate warning below. */
if (fncode == BUILT_IN_STRNCAT
&& bndrng[0] != bndrng[1]
&& wi::ltu_p (wi::to_offset (bndrng[0]), asize)
&& (!known_size
|| wi::ltu_p (asize, wibnd)))
{
asize = wibnd;
bound = NULL_TREE;
fncode = 0;
}
bool warned = false;
if (wi::ltu_p (asize, wibnd))
warned = warning_at (loc, OPT_Wstringop_overflow_,
"%qD argument %i declared attribute %<nonstring%> "
"is smaller than the specified bound %E",
fndecl, argno + 1, bndrng[0]);
{
if (bndrng[0] == bndrng[1])
warned = warning_at (loc, OPT_Wstringop_overflow_,
"%qD argument %i declared attribute "
"%<nonstring%> is smaller than the specified "
"bound %wu",
fndecl, argno + 1, wibnd.to_uhwi ());
else if (wi::ltu_p (asize, wi::to_offset (bndrng[0])))
warned = warning_at (loc, OPT_Wstringop_overflow_,
"%qD argument %i declared attribute "
"%<nonstring%> is smaller than "
"the specified bound [%E, %E]",
fndecl, argno + 1, bndrng[0], bndrng[1]);
else
warned = warning_at (loc, OPT_Wstringop_overflow_,
"%qD argument %i declared attribute "
"%<nonstring%> may be smaller than "
"the specified bound [%E, %E]",
fndecl, argno + 1, bndrng[0], bndrng[1]);
}
else if (fncode == BUILT_IN_STRNCAT)
; /* Avoid warning for calls to strncat() when the bound
is equal to the size of the non-string argument. */
else if (!bound)
warned = warning_at (loc, OPT_Wstringop_overflow_,
"%qD argument %i declared attribute %<nonstring%>",
......
2018-06-18 Martin Sebor <msebor@redhat.com>
PR middle-end/85602
* gcc.dg/attr-nonstring-2.c: Adjust text of expected warning.
* c-c++-common/attr-nonstring-8.c: New test.
2018-06-18 Martin Sebor <msebor@redhat.com>
PR tree-optimization/81384
* gcc.c-torture/execute/builtins/lib/strnlen.c: New test.
* gcc.c-torture/execute/builtins/strnlen-lib.c: New test.
......
/* PR middle-end/85602 - -Wsizeof-pointer-memaccess for strncat with size
of source
{ dg-do compile }
{ dg-options "-O2 -Wno-array-bounds -Wsizeof-pointer-memaccess -Wstringop-truncation -ftrack-macro-expansion=0" } */
#include "../gcc.dg/range.h"
typedef __SIZE_TYPE__ size_t;
#if __cplusplus
extern "C" {
#endif
char* strcpy (char*, const char*);
size_t strlen (const char*);
char* strncat (char*, const char*, __SIZE_TYPE__);
char* strncpy (char*, const char*, __SIZE_TYPE__);
#if __cplusplus
}
#endif
#define NONSTR __attribute__ ((nonstring))
NONSTR char nd3[3];
NONSTR char nd4[4];
NONSTR char nd5[5];
NONSTR char ns3[3];
NONSTR char ns4[4];
NONSTR char ns5[5];
NONSTR char* pns;
void sink (void*, ...);
#define T(call) sink (call)
/* Verify that for a nonstring source array of an unknown length
a warning is issued only when the bound exceeds the array size. */
void test_strncat_nonstring_cst (char *d)
{
T (strncat (d, ns3, 1));
T (strncat (d, ns3, 2));
T (strncat (d, ns3, 3));
T (strncat (d, ns3, sizeof ns3));
T (strncat (d, ns3, 4)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 4" } */
T (strncat (d, ns4, 1));
T (strncat (d, ns4, 2));
T (strncat (d, ns4, 3));
T (strncat (d, ns4, 4));
T (strncat (d, ns4, sizeof ns4));
T (strncat (d, ns4, 5)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */
T (strncat (nd3, ns3, 1));
T (strncat (nd3, ns3, 2));
T (strncat (nd3, ns3, 3)); /* { dg-warning "specified bound 3 equals destination size" } */
T (strncat (nd3, ns3, 4)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 4" } */
/* { dg-warning "specified bound 4 exceeds destination size 3" "" { target *-*-* } .-1 } */
T (strncat (d, pns, sizeof pns)); /* { dg-warning "argument to .sizeof. in .strncat. call is the same expression as the source" } */
}
void test_strncat_nonstring_var (char *d, size_t n)
{
/* In the following the bound coulld apply to either the destination
or the source. The expected use of strncat() is to pass it as
the bound DSIZE - strlen(D) - 1 so the call below is diagnosed. */
T (strncat (d, ns3, n)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
T (strncat (d, ns3, UR (0, 1)));
T (strncat (d, ns3, UR (1, 2)));
T (strncat (d, ns3, UR (2, 3)));
T (strncat (d, ns3, UR (3, 4))); /* { dg-warning "argument 2 declared attribute 'nonstring' may be smaller than the specified bound \\\[3, 4]" } */
T (strncat (d, ns3, UR (4, 5))); /* { dg-warning "argument 2 declared attribute 'nonstring' is smaller than the specified bound \\\[4, 5]" } */
/* Verify that the call below (the intended use of strncat()) is
also diagnosed. */
T (strncat (d, ns3, 256 - strlen (d) - 1)); /* { dg-warning "argument 2 declared attribute .nonstring." } */
T (strncat (nd3, ns5, UR (0, 1)));
T (strncat (nd3, ns5, UR (1, 2)));
T (strncat (nd3, ns5, UR (2, 3)));
T (strncat (nd3, ns5, UR (3, 4)));
T (strncat (nd3, ns5, UR (4, 5))); /* { dg-warning "specified bound between 4 and 5 exceeds destination size 3" } */
T (strncat (nd5, ns3, UR (0, 1)));
T (strncat (nd5, ns3, UR (1, 2)));
T (strncat (nd5, ns3, UR (2, 3)));
T (strncat (nd5, ns3, UR (3, 4))); /* { dg-warning "argument 2 declared attribute 'nonstring' may be smaller than the specified bound \\\[3, 4]" } */
}
/* Verify that for a nonstring source array of a known length (i.e.,
a nonstring array containing a nul-terminated string) a warning
is issued only for certain truncation.
The test cases are split up to work around bug 81343 (or one like
it). */
void test_strncat_string_1_1 (char *d)
{
strcpy (ns3, "1");
T (strncat (d, ns3, 1)); /* { dg-warning "output truncated before terminating nul copying 1 byte from a string of the same length" } */
}
void test_strncat_string_1_2 (char *d)
{
strcpy (ns3, "1");
T (strncat (d, ns3, 2));
}
void test_strncat_string_1_3 (char *d)
{
strcpy (ns3, "1");
T (strncat (d, ns3, 3));
}
void test_strncat_string_2_1 (char *d)
{
strcpy (ns3, "12");
T (strncat (d, ns3, 1)); /* { dg-warning "output truncated copying 1 byte from a string of length 2" } */
}
void test_strncat_string_2_2 (char *d)
{
strcpy (ns3, "12");
T (strncat (d, ns3, 2)); /* { dg-warning "output truncated before terminating nul copying 2 bytes from a string of the same length" } */
}
void test_strncat_string_2_3 (char *d)
{
strcpy (ns3, "12");
T (strncat (d, ns3, 3));
}
void test_strcncpy_nonstring_cst (char *d)
{
T (strncpy (d, ns3, 1));
T (strncpy (d, ns3, 2));
T (strncpy (d, ns3, 3));
T (strncpy (d, ns3, sizeof ns3));
T (strncpy (d, ns3, 4)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 4" } */
}
......@@ -52,7 +52,7 @@ void test_strnlen_array_range (void)
T (strnlen (ns3, UR (0, 9)));
T (strnlen (ns3, UR (3, 4)));
T (strnlen (ns3, UR (3, DIFF_MAX)));
T (strnlen (ns3, UR (4, 5))); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */
T (strnlen (ns3, UR (4, 5))); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[4, 5]" } */
T (strnlen (ns3, UR (DIFF_MAX, SIZE_MAX))); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller " } */
}
......@@ -110,6 +110,6 @@ void test_strnlen_string_range (void)
{
T (3, "1", 2, UR (0, 1));
T (3, "1", 2, UR (3, 9));
T (3, "123", 3, UR (4, 5)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" } */
T (3, "123", 3, UR (5, 9)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */
T (3, "123", 3, UR (4, 5)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[4, 5]" } */
T (3, "123", 3, UR (5, 9)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[5, 9]" } */
}
......@@ -1757,7 +1757,7 @@ handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
assumed to be the argument in some call to strlen() whose relationship
to SRC is being ascertained. */
static bool
bool
is_strlen_related_p (tree src, tree len)
{
if (TREE_CODE (TREE_TYPE (len)) == POINTER_TYPE
......@@ -1794,12 +1794,20 @@ is_strlen_related_p (tree src, tree len)
&& (code == BIT_AND_EXPR
|| code == NOP_EXPR)))
{
/* Pointer plus (an integer) and integer cast or truncation are
considered among the (potentially) related expressions to strlen.
Others are not. */
/* Pointer plus (an integer), and truncation are considered among
the (potentially) related expressions to strlen. */
return is_strlen_related_p (src, rhs1);
}
if (tree rhs2 = gimple_assign_rhs2 (def_stmt))
{
/* Integer subtraction is considered strlen-related when both
arguments are integers and second one is strlen-related. */
rhstype = TREE_TYPE (rhs2);
if (INTEGRAL_TYPE_P (rhstype) && code == MINUS_EXPR)
return is_strlen_related_p (src, rhs2);
}
return false;
}
......@@ -1870,9 +1878,22 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
if (TREE_CODE (dstdecl) == ADDR_EXPR)
dstdecl = TREE_OPERAND (dstdecl, 0);
/* If the destination refers to a an array/pointer declared nonstring
return early. */
tree ref = NULL_TREE;
if (!sidx)
{
/* If the source is a non-string return early to avoid warning
for possible truncation (if the truncation is certain SIDX
is non-zero). */
tree srcdecl = gimple_call_arg (stmt, 1);
if (TREE_CODE (srcdecl) == ADDR_EXPR)
srcdecl = TREE_OPERAND (srcdecl, 0);
if (get_attr_nonstring_decl (srcdecl, &ref))
return false;
}
/* Likewise, if the destination refers to a an array/pointer declared
nonstring return early. */
if (get_attr_nonstring_decl (dstdecl, &ref))
return false;
......
......@@ -21,6 +21,7 @@
#ifndef GCC_TREE_SSA_STRLEN_H
#define GCC_TREE_SSA_STRLEN_H
extern bool is_strlen_related_p (tree, tree);
extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
#endif // GCC_TREE_SSA_STRLEN_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment