PR middle-end/85602 - -Wsizeof-pointer-memaccess for strncat with size of source
authorMartin Sebor <msebor@redhat.com>
Mon, 18 Jun 2018 22:17:57 +0000 (22:17 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Mon, 18 Jun 2018 22:17:57 +0000 (16:17 -0600)
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

gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-warn.c
gcc/calls.c
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/attr-nonstring-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-nonstring-2.c
gcc/tree-ssa-strlen.c
gcc/tree-ssa-strlen.h

index fa80e8789f2a4408cb4448898987ee0f3b4c0cc9..239d08aa6a181773a74b7ee0e7af782999a5f955 100644 (file)
@@ -1,3 +1,12 @@
+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
index d4e5942e49d1dabad060e998b4f5b059636957c2..18388517e12f31805e0552d829fd3dfcc84e9a65 100644 (file)
@@ -1,3 +1,9 @@
+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.
index 95ac09d76a1e7ed67a94c9dd97152e07417e046f..d34d6672937c539c394caee44369e983ab4315d5 100644 (file)
@@ -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);
index 02562ddc1ee87029e0ce59b6f965eb5e89f5c5a9..1970f1c51ddfdf3e156e734d248ce41a9bcfd841 100644 (file)
@@ -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%>",
index 024cf6b2437e79b34b3699621b10e31247f8d781..b3b13226d2779b442e44c741b99614046952890c 100644 (file)
@@ -1,3 +1,9 @@
+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
diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-8.c b/gcc/testsuite/c-c++-common/attr-nonstring-8.c
new file mode 100644 (file)
index 0000000..15b68ed
--- /dev/null
@@ -0,0 +1,147 @@
+/* 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" } */
+}
index e9edb661b9aec0c5d64ac10ac23108e440eff914..ef2144d62075fdafbd5568a804e0c45844afa119 100644 (file)
@@ -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]" } */
 }
index 693d9d3491a273a3fb9517e167579b65fcaae640..92545b9ce4d03af5d3fbe296b38b587048476291 100644 (file)
@@ -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;
 
index 1399a7819eb7e4107be3ec49902f10f5911c28b1..f427fb741a5f52a778268a89b06a79e7b074375c 100644 (file)
@@ -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