+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.
#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
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);
#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"
/* 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:
}
/* Fall through. */
+ case BUILT_IN_STRNCAT:
case BUILT_IN_STPNCPY:
case BUILT_IN_STRNCPY:
if (2 < nargs)
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. */
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;
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
--- /dev/null
+/* 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" } */
+}
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 " } */
}
{
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]" } */
}
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
&& (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;
}
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;
#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