Correct and improve -Wnonnull for calls to functions with VLA arguments (PR middle...
authorMartin Sebor <msebor@redhat.com>
Tue, 29 Sep 2020 23:10:54 +0000 (17:10 -0600)
committerMartin Sebor <msebor@redhat.com>
Tue, 29 Sep 2020 23:13:55 +0000 (17:13 -0600)
Resolves:
PR middle-end/97188 - ICE passing a null VLA to a function expecting at least one element

gcc/ChangeLog:

PR middle-end/97188
* calls.c (maybe_warn_rdwr_sizes): Simplify warning messages.
Correct handling of VLA argumments.

gcc/testsuite/ChangeLog:

PR middle-end/97188
* gcc.dg/Wstringop-overflow-23.c: Adjust text of expected warnings.
* gcc.dg/Wnonnull-4.c: New test.

gcc/calls.c
gcc/testsuite/gcc.dg/Wnonnull-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-overflow-23.c

index 0e5c696c4638febea5a854b210ae615831eb3523..ed4363811c8248171579f8f392b9347497eeab85 100644 (file)
@@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
+#define INCLUDE_STRING
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
@@ -1924,7 +1925,10 @@ static void
 maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
 {
   auto_diagnostic_group adg;
-  bool warned = false;
+
+  /* Set if a warning has been issued for any argument (used to decide
+     whether to emit an informational note at the end).  */
+  bool any_warned = false;
 
   /* A string describing the attributes that the warnings issued by this
      function apply to.  Used to print one informational note per function
@@ -1974,27 +1978,60 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
       else
        access_size = rwm->get (sizidx)->size;
 
-      bool warned = false;
+      /* Format the value or range to avoid an explosion of messages.  */
+      char sizstr[80];
+      tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
+      if (get_size_range (access_size, sizrng, true))
+       {
+         const char *s0 = print_generic_expr_to_str (sizrng[0]);
+         if (tree_int_cst_equal (sizrng[0], sizrng[1]))
+           {
+             gcc_checking_assert (strlen (s0) < sizeof sizstr);
+             strcpy (sizstr, s0);
+           }
+         else
+           {
+             const char *s1 = print_generic_expr_to_str (sizrng[1]);
+             gcc_checking_assert (strlen (s0) + strlen (s1)
+                                  < sizeof sizstr - 4);
+             sprintf (sizstr, "[%s, %s]", s0, s1);
+           }
+       }
+      else
+       *sizstr = '\0';
+
+      /* Set if a warning has been issued for the current argument.  */
+      bool arg_warned = false;
       location_t loc = EXPR_LOCATION (exp);
       tree ptr = access.second.ptr;
-      tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
-      if (get_size_range (access_size, sizrng, true)
+      if (*sizstr
          && tree_int_cst_sgn (sizrng[0]) < 0
          && tree_int_cst_sgn (sizrng[1]) < 0)
        {
          /* Warn about negative sizes.  */
-         if (tree_int_cst_equal (sizrng[0], sizrng[1]))
-           warned = warning_at (loc, OPT_Wstringop_overflow_,
-                                "%Kargument %i value %E is negative",
-                                exp, sizidx + 1, access_size);
+         if (access.second.internal_p)
+           {
+             const std::string argtypestr
+               = access.second.array_as_string (ptrtype);
+
+             arg_warned = warning_at (loc, OPT_Wstringop_overflow_,
+                                      "%Kbound argument %i value %s is "
+                                      "negative for a variable length array "
+                                      "argument %i of type %s",
+                                      exp, sizidx + 1, sizstr,
+                                      ptridx + 1, argtypestr.c_str ());
+           }
          else
-           warned = warning_at (loc, OPT_Wstringop_overflow_,
-                                "%Kargument %i range [%E, %E] is negative",
-                                exp, sizidx + 1, sizrng[0], sizrng[1]);
-         if (warned)
+           arg_warned = warning_at (loc, OPT_Wstringop_overflow_,
+                                    "%Kargument %i value %s is negative",
+                                    exp, sizidx + 1, sizstr);
+
+         if (arg_warned)
            {
              append_attrname (access, attrstr, sizeof attrstr);
-             /* Avoid warning again for the same attribute.  */
+             /* Remember a warning has been issued and avoid warning
+                again below for the same attribute.  */
+             any_warned = true;
              continue;
            }
        }
@@ -2006,7 +2043,6 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
              /* Multiply ACCESS_SIZE by the size of the type the pointer
                 argument points to.  If it's incomplete the size is used
                 as is.  */
-             access_size = NULL_TREE;
              if (tree argsize = TYPE_SIZE_UNIT (argtype))
                if (TREE_CODE (argsize) == INTEGER_CST)
                  {
@@ -2028,35 +2064,44 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
                 different from also declaring the pointer argument with
                 attribute nonnull when the function accepts null pointers
                 only when the corresponding size is zero.  */
-             if (tree_int_cst_equal (sizrng[0], sizrng[1]))
-               warned = warning_at (loc, OPT_Wnonnull,
-                                    "%Kargument %i is null but "
-                                    "the corresponding size argument %i "
-                                    "value is %E",
-                                    exp, ptridx + 1, sizidx + 1, access_size);
+             if (access.second.internal_p)
+               {
+                 const std::string argtypestr
+                   = access.second.array_as_string (ptrtype);
+
+                 arg_warned = warning_at (loc, OPT_Wnonnull,
+                                          "%Kargument %i of variable length "
+                                          "array %s is null but "
+                                          "the corresponding bound argument "
+                                          "%i value is %s",
+                                          exp, sizidx + 1, argtypestr.c_str (),
+                                          ptridx + 1, sizstr);
+               }
              else
-               warned = warning_at (loc, OPT_Wnonnull,
-                                    "%Kargument %i is null but "
-                                    "the corresponding size argument %i "
-                                    "range is [%E, %E]",
-                                    exp, ptridx + 1, sizidx + 1,
-                                    sizrng[0], sizrng[1]);
+               arg_warned = warning_at (loc, OPT_Wnonnull,
+                                        "%Kargument %i is null but "
+                                        "the corresponding size argument "
+                                        "%i value is %s",
+                                        exp, ptridx + 1, sizidx + 1,
+                                        sizstr);
            }
          else if (access_size && access.second.static_p)
            {
              /* Warn about null pointers for [static N] array arguments
                 but do not warn for ordinary (i.e., nonstatic) arrays.  */
-             warned = warning_at (loc, OPT_Wnonnull,
-                                  "%Kargument %i to %<%T[static %E]%> null "
-                                  "where non-null expected",
-                                  exp, ptridx + 1, argtype,
-                                  sizrng[0]);
+             arg_warned = warning_at (loc, OPT_Wnonnull,
+                                      "%Kargument %i to %<%T[static %E]%> "
+                                      "is null where non-null expected",
+                                      exp, ptridx + 1, argtype,
+                                      access_size);
            }
 
-         if (warned)
+         if (arg_warned)
            {
              append_attrname (access, attrstr, sizeof attrstr);
-             /* Avoid warning again for the same attribute.  */
+             /* Remember a warning has been issued and avoid warning
+                again below for the same attribute.  */
+             any_warned = true;
              continue;
            }
        }
@@ -2101,7 +2146,7 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
 
       if (TREE_NO_WARNING (exp))
        {
-         warned = true;
+         any_warned = true;
 
          if (access.second.internal_p)
            inform (loc, "referencing argument %u of type %qT",
@@ -2124,7 +2169,7 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, tree exp)
                "in a call with type %qT and attribute %qs",
                fntype, attrstr);
     }
-  else if (warned)
+  else if (any_warned)
     {
       if (fndecl)
        inform (DECL_SOURCE_LOCATION (fndecl),
diff --git a/gcc/testsuite/gcc.dg/Wnonnull-4.c b/gcc/testsuite/gcc.dg/Wnonnull-4.c
new file mode 100644 (file)
index 0000000..180a40d
--- /dev/null
@@ -0,0 +1,173 @@
+/* PR middle-end/97188 - ICE passing a null VLA to a function expecting
+   at least one element
+   { dg-do compile }
+   { dg-options "-O -Wall -ftrack-macro-expansion=0" } */
+
+#define INT_MAX   __INT_MAX__
+#define INT_MIN   (-INT_MAX - 1)
+
+/* Exercise passing nul to a one-dimensional VLA argument.  */
+
+void test_fca_n (int r_m1)
+{
+  extern void fca_n (int n, char[n]);  // { dg-message "in a call to function 'fca_n'" "note" }
+
+#define T(n) fca_n (n, 0)
+
+  int min = INT_MIN;
+  int max = INT_MAX;
+  if (r_m1 >= 0)
+    r_m1 = -1;
+
+  // Verify negative bounds.
+  T (min);          // { dg-warning "bound argument 1 value -\\d+ is negative for a variable length array argument 2 of type 'char\\\[n]'" }
+  T (r_m1);         // { dg-warning "bound argument 1 value \\\[-\\d+, -1] is negative for a variable length array argument 2 of type 'char\\\[n]" }
+  T ( -1);          // { dg-warning "bound argument 1 value -1 is negative for a variable length array argument 2 of type 'char\\\[n]" }
+
+  T (  0);
+
+  // Verify positive bounds.
+  T (  1);          // { dg-warning "argument 1 of variable length array 'char\\\[n]' is null but the corresponding bound argument 2 value is 1" }
+  T (  9);          // { dg-warning "argument 1 of variable length array 'char\\\[n]' is null but the corresponding bound argument 2 value is 9" }
+  T (max);          // { dg-warning "argument 1 of variable length array 'char\\\[n]' is null but the corresponding bound argument 2 value is \\d+" }
+}
+
+
+/* Exercise passing nul to an array with unspecified bound of VLAs.  */
+
+void test_fsa_x_n (int r_m1)
+{
+  extern void fsa_x_n (int n, short[][n]);   // { dg-message "in a call to function 'fsa_x_n'" "note" }
+
+#undef T
+#define T(n) fsa_x_n (n, 0)
+
+  int min = INT_MIN;
+  int max = INT_MAX;
+  if (r_m1 >= 0)
+    r_m1 = -1;
+
+  // Verify negative bounds.
+  T (min);          // { dg-warning "bound argument 1 value -\\d+ is negative for a variable length array argument 2 of type 'short int\\\[]\\\[n]'" }
+  T (r_m1);         // { dg-warning "bound argument 1 value \\\[-\\d+, -1] is negative for a variable length array argument 2 of type 'short int\\\[]\\\[n]" }
+  T ( -1);          // { dg-warning "bound argument 1 value -1 is negative for a variable length array argument 2 of type 'short int\\\[]\\\[n]" }
+
+  T (  0);
+
+  // Verify positive bounds.
+  T (  1);          // { dg-warning "argument 1 of variable length array 'short int\\\[]\\\[n]' is null but the corresponding bound argument 2 value is 1" }
+  T (  9);          // { dg-warning "argument 1 of variable length array 'short int\\\[]\\\[n]' is null but the corresponding bound argument 2 value is 9" }
+  T (max);          // { dg-warning "argument 1 of variable length array 'short int\\\[]\\\[n]' is null but the corresponding bound argument 2 value is \\d+" }
+}
+
+
+/* Exercise passing nul to an array of a single VLA.  */
+
+void test_fia_1_n (int r_m1)
+{
+  extern void fia_1_n (int n, int[1][n]);  // { dg-message "in a call to function 'fia_1_n'" "note" }
+
+#undef T
+#define T(n) fia_1_n (n, 0)
+
+  int min = INT_MIN;
+  int max = INT_MAX;
+  if (r_m1 >= 0)
+    r_m1 = -1;
+
+  // Verify negative bounds.
+  T (min);          // { dg-warning "bound argument 1 value -\\d+ is negative for a variable length array argument 2 of type 'int\\\[1]\\\[n]'" }
+  T (r_m1);         // { dg-warning "bound argument 1 value \\\[-\\d+, -1] is negative for a variable length array argument 2 of type 'int\\\[1]\\\[n]" }
+  T ( -1);          // { dg-warning "bound argument 1 value -1 is negative for a variable length array argument 2 of type 'int\\\[1]\\\[n]" }
+
+  T (  0);
+
+  // Verify positive bounds.
+  T (  1);          // { dg-warning "argument 1 of variable length array 'int\\\[1]\\\[n]' is null but the corresponding bound argument 2 value is 1" }
+  T (  9);          // { dg-warning "argument 1 of variable length array 'int\\\[1]\\\[n]' is null but the corresponding bound argument 2 value is 9" }
+  T (max);          // { dg-warning "argument 1 of variable length array 'int\\\[1]\\\[n]' is null but the corresponding bound argument 2 value is \\d+" }
+}
+
+
+/* Exercise passing nul to an array of three VLAs.  */
+
+void test_fla_3_n (int r_m1)
+{
+  extern void fla_3_n (int n, long[3][n]);  // { dg-message "in a call to function 'fla_3_n'" "note" }
+
+#undef T
+#define T(n) fla_3_n (n, 0)
+
+  int min = INT_MIN;
+  int max = INT_MAX;
+  if (r_m1 >= 0)
+    r_m1 = -1;
+
+  // Verify negative bounds.
+  T (min);          // { dg-warning "bound argument 1 value -\\d+ is negative for a variable length array argument 2 of type 'long int\\\[3]\\\[n]'" }
+  T (r_m1);         // { dg-warning "bound argument 1 value \\\[-\\d+, -1] is negative for a variable length array argument 2 of type 'long int\\\[3]\\\[n]" }
+  T ( -1);          // { dg-warning "bound argument 1 value -1 is negative for a variable length array argument 2 of type 'long int\\\[3]\\\[n]" }
+
+  T (  0);
+
+  // Verify positive bounds.
+  T (  1);          // { dg-warning "argument 1 of variable length array 'long int\\\[3]\\\[n]' is null but the corresponding bound argument 2 value is 1" }
+  T (  9);          // { dg-warning "argument 1 of variable length array 'long int\\\[3]\\\[n]' is null but the corresponding bound argument 2 value is 9" }
+  T (max);          // { dg-warning "argument 1 of variable length array 'long int\\\[3]\\\[n]' is null but the corresponding bound argument 2 value is \\d+" }
+}
+
+
+/* Exercise passing nul to a VLA of five-element arrays.  */
+
+void test_fda_n_5 (int r_m1)
+{
+  extern void fda_n_5 (int n, double[n][5]);// { dg-message "in a call to function 'fda_n_5'" "note" }
+
+#undef T
+#define T(n) fda_n_5 (n, 0)
+
+  int min = INT_MIN;
+  int max = INT_MAX;
+  if (r_m1 >= 0)
+    r_m1 = -1;
+
+  // Verify negative bounds.
+  T (min);          // { dg-warning "bound argument 1 value -\\d+ is negative for a variable length array argument 2 of type 'double\\\[n]\\\[5]'" }
+  T (r_m1);         // { dg-warning "bound argument 1 value \\\[-\\d+, -1] is negative for a variable length array argument 2 of type 'double\\\[n]\\\[5]" }
+  T ( -1);          // { dg-warning "bound argument 1 value -1 is negative for a variable length array argument 2 of type 'double\\\[n]\\\[5]" }
+
+  T (  0);
+
+  // Verify positive bounds.
+  T (  1);          // { dg-warning "argument 1 of variable length array 'double\\\[n]\\\[5]' is null but the corresponding bound argument 2 value is 1" }
+  T (  9);          // { dg-warning "argument 1 of variable length array 'double\\\[n]\\\[5]' is null but the corresponding bound argument 2 value is 9" }
+  T (max);          // { dg-warning "argument 1 of variable length array 'double\\\[n]\\\[5]' is null but the corresponding bound argument 2 value is \\d+" }
+}
+
+
+/* Exercise passing nul to a two-dimensional VLA.  */
+
+void test_fca_n_n (int r_m1)
+{
+  extern void fca_n_n (int n, char[n][n]);  // { dg-message "in a call to function 'fca_n_n'" "note" }
+
+#undef T
+#define T(n) fca_n_n (n, 0)
+
+  int min = INT_MIN;
+  int max = INT_MAX;
+  if (r_m1 >= 0)
+    r_m1 = -1;
+
+  // Verify negative bounds.
+  T (min);          // { dg-warning "bound argument 1 value -\\d+ is negative for a variable length array argument 2 of type 'char\\\[n]\\\[n]'" }
+  T (r_m1);         // { dg-warning "bound argument 1 value \\\[-\\d+, -1] is negative for a variable length array argument 2 of type 'char\\\[n]\\\[n]" }
+  T ( -1);          // { dg-warning "bound argument 1 value -1 is negative for a variable length array argument 2 of type 'char\\\[n]\\\[n]" }
+
+  T (  0);
+
+  // Verify positive bounds.
+  T (  1);          // { dg-warning "argument 1 of variable length array 'char\\\[n]\\\[n]' is null but the corresponding bound argument 2 value is 1" }
+  T (  9);          // { dg-warning "argument 1 of variable length array 'char\\\[n]\\\[n]' is null but the corresponding bound argument 2 value is 9" }
+  T (max);          // { dg-warning "argument 1 of variable length array 'char\\\[n]\\\[n]' is null but the corresponding bound argument 2 value is \\d+" }
+}
index bbc12102d1406294e2a9fb0e5c304b84498cfc61..0da916ad993c9dc42f48b35f552f3b568f521fc8 100644 (file)
@@ -40,7 +40,11 @@ void test_rd2_1 (void)
 
   {
     void *null = 0;
-    rd2_1 (SR (1, 2), null);    // { dg-warning "argument 2 is null but the corresponding size argument 1 range is \\\[1, 2]" }
+    /* Ideally the message would say "range" for a range and "value"
+       for a singular value but using the same reduces the complexity
+       of the code and keeps down the number of messages that need to
+       be translated, withot sacrificing (too much) clarity.  */
+    rd2_1 (SR (1, 2), null);    // { dg-warning "argument 2 is null but the corresponding size argument 1 range|value is \\\[1, 2]" }
   }
 }
 
@@ -59,7 +63,7 @@ void test_wr3_1 (void)
 
   void *null = 0;
 
-  wr3_1 (SR (1, 2), 1, null);   // { dg-warning "argument 3 is null but the corresponding size argument 1 range is \\\[1, 2]" }
+  wr3_1 (SR (1, 2), 1, null);   // { dg-warning "argument 3 is null but the corresponding size argument 1 range|value is \\\[1, 2]" }
 }
 
 
@@ -71,7 +75,7 @@ void test_wrd2_1 (int n)
   wr2_1 (0, 0);
   wr2_1 (SR (-1, 1), 0);
   wr2_1 (SR (0, 1), 0);
-  wr2_1 (SR (1, 2), 0);         // { dg-warning "argument 2 is null but the corresponding size argument 1 range is \\\[1, 2]" }
+  wr2_1 (SR (1, 2), 0);         // { dg-warning "argument 2 is null but the corresponding size argument 1 range|value is \\\[1, 2]" }
 
   /* This should probably be diagnosed but to avoid false positives
      caused by jump threading and such it would have to be done
@@ -127,7 +131,7 @@ void test_rd1_3_wr2_4 (const void *s, void *d, int n1, int n2)
   rd1_3_wr2_4 (s, d, -1, 2);    // { dg-warning "argument 3 value -1 is negative" }
 
   const int ir_min_m1 = SR (INT_MIN, -1);
-  rd1_3_wr2_4 (s, d, ir_min_m1, 2);   // { dg-warning "argument 3 range \\\[-\[0-9\]+, -1] is negative" }
+  rd1_3_wr2_4 (s, d, ir_min_m1, 2);   // { dg-warning "argument 3 range|value \\\[-\[0-9\]+, -1] is negative" }
 
   rd1_3_wr2_4 (s, d, SR (-1, 0), 2);
   rd1_3_wr2_4 (s, d, SR (INT_MIN, INT_MAX), 2);