re PR c/16351 (NULL dereference warnings)
authorManuel López-Ibáñez <manu@gcc.gnu.org>
Wed, 5 Aug 2015 17:36:29 +0000 (17:36 +0000)
committerManuel López-Ibáñez <manu@gcc.gnu.org>
Wed, 5 Aug 2015 17:36:29 +0000 (17:36 +0000)
gcc/ChangeLog:

2015-08-05  Manuel López-Ibáñez  <manu@gcc.gnu.org>
    Jeff Law  <law@redhat.com>

PR c/16351
* doc/invoke.texi (Wnull-dereference): New.
* tree-vrp.c (infer_value_range): Update call to infer_nonnull_range.
* gimple-ssa-isolate-paths.c (find_implicit_erroneous_behaviour):
Warn for potential NULL dereferences.
(find_explicit_erroneous_behaviour): Warn for NULL dereferences.
* ubsan.c (instrument_nonnull_arg): Call
infer_nonnull_range_by_attribute.
(instrument_nonnull_return): Likewise.
* common.opt (Wnull-dereference); New.
* gimple.c (infer_nonnull_range): Remove bool arguments.
(infer_nonnull_range_by_dereference): New.
(infer_nonnull_range_by_attribute): New.
* gimple.h: Update declarations.

gcc/testsuite/ChangeLog:

2015-08-05  Manuel López-Ibáñez  <manu@gcc.gnu.org>
    Jeff Law  <law@redhat.com>

PR c/16351
* gcc.dg/tree-ssa/isolate-2.c: Close comment.
* gcc.dg/tree-ssa/isolate-4.c: Likewise.
* gcc.dg/tree-ssa/wnull-dereference.c: New test.
* gcc.dg/tree-ssa/isolate-1.c: Test warnings with -Wnull-dereference.
* gcc.dg/tree-ssa/isolate-3.c: Likewise.
* gcc.dg/tree-ssa/isolate-5.c: Likewise.

Co-Authored-By: Jeff Law <law@redhat.com>
From-SVN: r226640

15 files changed:
gcc/ChangeLog
gcc/common.opt
gcc/doc/invoke.texi
gcc/gimple-ssa-isolate-paths.c
gcc/gimple.c
gcc/gimple.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/tree-ssa/isolate-1.c
gcc/testsuite/gcc.dg/tree-ssa/isolate-2.c
gcc/testsuite/gcc.dg/tree-ssa/isolate-3.c
gcc/testsuite/gcc.dg/tree-ssa/isolate-4.c
gcc/testsuite/gcc.dg/tree-ssa/isolate-5.c
gcc/testsuite/gcc.dg/tree-ssa/wnull-dereference.c [new file with mode: 0644]
gcc/tree-vrp.c
gcc/ubsan.c

index 38c3fdc8947e05f70d2362bd9334c010576e43c2..c29e1c6e4da0672925862fb603cab188f6975f52 100644 (file)
@@ -1,3 +1,21 @@
+2015-08-05  Manuel López-Ibáñez  <manu@gcc.gnu.org>
+           Jeff Law  <law@redhat.com>
+
+       PR c/16351
+       * doc/invoke.texi (Wnull-dereference): New.
+       * tree-vrp.c (infer_value_range): Update call to infer_nonnull_range.
+       * gimple-ssa-isolate-paths.c (find_implicit_erroneous_behaviour):
+       Warn for potential NULL dereferences.
+       (find_explicit_erroneous_behaviour): Warn for NULL dereferences.
+       * ubsan.c (instrument_nonnull_arg): Call
+       infer_nonnull_range_by_attribute.
+       (instrument_nonnull_return): Likewise.
+       * common.opt (Wnull-dereference); New.
+       * gimple.c (infer_nonnull_range): Remove bool arguments.
+       (infer_nonnull_range_by_dereference): New.
+       (infer_nonnull_range_by_attribute): New.
+       * gimple.h: Update declarations.
+
 2015-08-05  Richard Sandiford  <richard.sandiford@arm.com>
 
        * gensupport.c (sequence_num): Replace with...
index 8f25f8b35cf698e69a59b233113b7f37079cea1e..fb9e9daa7542780f41e7acd48ef8f4d640d2cdc0 100644 (file)
@@ -592,6 +592,10 @@ Wlarger-than=
 Common RejectNegative Joined UInteger Warning
 -Wlarger-than=<number> Warn if an object is larger than <number> bytes
 
+Wnull-dereference
+Common Var(warn_null_dereference) Warning EnabledBy(Wall)
+Warn if dereferencing a NULL pointer may lead to erroneous or undefined behavior
+
 Wunsafe-loop-optimizations
 Common Var(warn_unsafe_loop_optimizations) Warning
 Warn if the loop cannot be optimized due to nontrivial assumptions.
index f7daa02d6587cc2a99e27322b41d88cfcbbd10fc..8f375b7e527cbb03c987090a0342b139cb34d1c2 100644 (file)
@@ -260,6 +260,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wimplicit  -Wimplicit-function-declaration  -Wimplicit-int @gol
 -Winit-self  -Winline  -Wno-int-conversion @gol
 -Wno-int-to-pointer-cast -Wno-invalid-offsetof @gol
+-Wnull-dereference @gol
 -Winvalid-pch -Wlarger-than=@var{len}  -Wunsafe-loop-optimizations @gol
 -Wlogical-op -Wlogical-not-parentheses -Wlong-long @gol
 -Wmain -Wmaybe-uninitialized -Wmemset-transposed-args @gol
@@ -4156,6 +4157,16 @@ In order to get a warning about an unused function parameter, you must
 either specify @option{-Wextra -Wunused} (note that @option{-Wall} implies
 @option{-Wunused}), or separately specify @option{-Wunused-parameter}.
 
+@item -Wnull-dereference
+@opindex Wnull-dereference
+@opindex Wno-null-dereference
+Warn if the compiler detects paths that trigger erroneous or
+undefined behavior due to dereferencing a null pointer.  This option
+is only active when @option{-fdelete-null-pointer-checks} is active,
+which is enabled by optimizations in most targets.  The precision of
+the warnings depends on the optimization options used.  This option is
+enabled by @option{-Wall}.
+
 @item -Wuninitialized
 @opindex Wuninitialized
 @opindex Wno-uninitialized
index 7d3758ce5abbe4d2ea406491b6bb461d85aa3b87..6f84f85856b932559c5c879fadb358e7363e5285 100644 (file)
@@ -331,11 +331,29 @@ find_implicit_erroneous_behaviour (void)
                  if (gimple_bb (use_stmt) != bb)
                    continue;
 
-                 if (infer_nonnull_range (use_stmt, lhs,
-                                          flag_isolate_erroneous_paths_dereference,
-                                          flag_isolate_erroneous_paths_attribute))
+                 bool by_dereference 
+                   = infer_nonnull_range_by_dereference (use_stmt, lhs);
 
+                 if (by_dereference 
+                     || infer_nonnull_range_by_attribute (use_stmt, lhs))
                    {
+                     location_t loc = gimple_location (use_stmt)
+                       ? gimple_location (use_stmt)
+                       : gimple_phi_arg_location (phi, i);
+
+                     if (by_dereference)
+                       {
+                         warning_at (loc, OPT_Wnull_dereference,
+                                     "potential null pointer dereference");
+                         if (!flag_isolate_erroneous_paths_dereference)
+                           continue;
+                       }
+                     else 
+                       {
+                         if (!flag_isolate_erroneous_paths_attribute)
+                           continue;
+                       }
+
                      duplicate = isolate_path (bb, duplicate, e,
                                                use_stmt, lhs, false);
 
@@ -381,13 +399,29 @@ find_explicit_erroneous_behaviour (void)
        {
          gimple stmt = gsi_stmt (si);
 
-         /* By passing null_pointer_node, we can use infer_nonnull_range
-            to detect explicit NULL pointer dereferences and other uses
-            where a non-NULL value is required.  */
-         if (infer_nonnull_range (stmt, null_pointer_node,
-                                  flag_isolate_erroneous_paths_dereference,
-                                  flag_isolate_erroneous_paths_attribute))
+         /* By passing null_pointer_node, we can use the
+            infer_nonnull_range functions to detect explicit NULL
+            pointer dereferences and other uses where a non-NULL
+            value is required.  */
+         
+         bool by_dereference
+           = infer_nonnull_range_by_dereference (stmt, null_pointer_node);
+         if (by_dereference
+             || infer_nonnull_range_by_attribute (stmt, null_pointer_node))
            {
+             if (by_dereference)
+               {
+                 warning_at (gimple_location (stmt), OPT_Wnull_dereference,
+                             "null pointer dereference");
+                 if (!flag_isolate_erroneous_paths_dereference)
+                   continue;
+               }
+             else
+               {
+                 if (!flag_isolate_erroneous_paths_attribute)
+                   continue;
+               }
+
              insert_trap_and_remove_trailing_statements (&si,
                                                          null_pointer_node);
 
@@ -534,7 +568,8 @@ public:
       /* If we do not have a suitable builtin function for the trap statement,
         then do not perform the optimization.  */
       return (flag_isolate_erroneous_paths_dereference != 0
-             || flag_isolate_erroneous_paths_attribute != 0);
+             || flag_isolate_erroneous_paths_attribute != 0
+             || warn_null_dereference);
     }
 
   virtual unsigned int execute (function *)
index 89291b07cf46a33866d65efcf6676e75a5177560..e31a273172792bd0ac1071b24baf491b0c56e033 100644 (file)
@@ -2618,16 +2618,20 @@ check_loadstore (gimple, tree op, tree, void *data)
   return false;
 }
 
-/* If OP can be inferred to be non-NULL after STMT executes, return true.
 
-   DEREFERENCE is TRUE if we can use a pointer dereference to infer a
-   non-NULL range, FALSE otherwise.
-
-   ATTRIBUTE is TRUE if we can use attributes to infer a non-NULL range
-   for function arguments and return values.  FALSE otherwise.  */
+/* Return true if OP can be inferred to be non-NULL after STMT executes,
+   either by using a pointer dereference or attributes.  */
+bool
+infer_nonnull_range (gimple stmt, tree op)
+{
+  return infer_nonnull_range_by_dereference (stmt, op)
+    || infer_nonnull_range_by_attribute (stmt, op);
+}
 
+/* Return true if OP can be inferred to be non-NULL after STMT
+   executes by using a pointer dereference.  */
 bool
-infer_nonnull_range (gimple stmt, tree op, bool dereference, bool attribute)
+infer_nonnull_range_by_dereference (gimple stmt, tree op)
 {
   /* We can only assume that a pointer dereference will yield
      non-NULL if -fdelete-null-pointer-checks is enabled.  */
@@ -2636,13 +2640,26 @@ infer_nonnull_range (gimple stmt, tree op, bool dereference, bool attribute)
       || gimple_code (stmt) == GIMPLE_ASM)
     return false;
 
-  if (dereference
-      && walk_stmt_load_store_ops (stmt, (void *)op,
-                                  check_loadstore, check_loadstore))
+  if (walk_stmt_load_store_ops (stmt, (void *)op,
+                               check_loadstore, check_loadstore))
     return true;
 
-  if (attribute
-      && is_gimple_call (stmt) && !gimple_call_internal_p (stmt))
+  return false;
+}
+
+/* Return true if OP can be inferred to be a non-NULL after STMT
+   executes by using attributes.  */
+bool
+infer_nonnull_range_by_attribute (gimple stmt, tree op)
+{
+  /* We can only assume that a pointer dereference will yield
+     non-NULL if -fdelete-null-pointer-checks is enabled.  */
+  if (!flag_delete_null_pointer_checks
+      || !POINTER_TYPE_P (TREE_TYPE (op))
+      || gimple_code (stmt) == GIMPLE_ASM)
+    return false;
+
+  if (is_gimple_call (stmt) && !gimple_call_internal_p (stmt))
     {
       tree fntype = gimple_call_fntype (stmt);
       tree attrs = TYPE_ATTRIBUTES (fntype);
@@ -2681,13 +2698,12 @@ infer_nonnull_range (gimple stmt, tree op, bool dereference, bool attribute)
 
   /* If this function is marked as returning non-null, then we can
      infer OP is non-null if it is used in the return statement.  */
-  if (attribute)
-    if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
-      if (gimple_return_retval (return_stmt)
-         && operand_equal_p (gimple_return_retval (return_stmt), op, 0)
-         && lookup_attribute ("returns_nonnull",
-                              TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))))
-       return true;
+  if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
+    if (gimple_return_retval (return_stmt)
+       && operand_equal_p (gimple_return_retval (return_stmt), op, 0)
+       && lookup_attribute ("returns_nonnull",
+                            TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl))))
+      return true;
 
   return false;
 }
index 596d28d31d19a718962a6dd45e754198f01adea1..d4467b5a163046cb99ead70ba5d6b5159054e75b 100644 (file)
@@ -1401,7 +1401,9 @@ extern bool gimple_call_builtin_p (const_gimple, enum built_in_function);
 extern bool gimple_asm_clobbers_memory_p (const gasm *);
 extern void dump_decl_set (FILE *, bitmap);
 extern bool nonfreeing_call_p (gimple);
-extern bool infer_nonnull_range (gimple, tree, bool, bool);
+extern bool infer_nonnull_range (gimple, tree);
+extern bool infer_nonnull_range_by_dereference (gimple, tree);
+extern bool infer_nonnull_range_by_attribute (gimple, tree);
 extern void sort_case_labels (vec<tree>);
 extern void preprocess_case_label_vec_for_gimple (vec<tree>, tree, tree *);
 extern void gimple_seq_set_location (gimple_seq, location_t);
index e81d1df5d5b0ebcf16d7241700ba877a3c5c743a..205975df36a6d1a7a930a251a8e59f27cc903461 100644 (file)
@@ -1,3 +1,14 @@
+2015-08-05  Manuel López-Ibáñez  <manu@gcc.gnu.org>
+           Jeff Law  <law@redhat.com>
+
+       PR c/16351
+       * gcc.dg/tree-ssa/isolate-2.c: Close comment.
+       * gcc.dg/tree-ssa/isolate-4.c: Likewise.
+       * gcc.dg/tree-ssa/wnull-dereference.c: New test.
+       * gcc.dg/tree-ssa/isolate-1.c: Test warnings with -Wnull-dereference.
+       * gcc.dg/tree-ssa/isolate-3.c: Likewise.
+       * gcc.dg/tree-ssa/isolate-5.c: Likewise.
+
 2015-08-05  Francois-Xavier Coudert  <fxcoudert@gcc.gnu.org>
 
        PR middle-end/66311
index 51ee328a08850530ffe6ccd7a7576a1d42fa1ac7..486c307cbdc30a2279dbc6eb3692eb259c8a318f 100644 (file)
@@ -1,6 +1,5 @@
-
 /* { dg-do compile } */ 
-/* { dg-options "-O2 -fdump-tree-isolate-paths -fdelete-null-pointer-checks" } */
+/* { dg-options "-O2 -fdump-tree-isolate-paths -fdelete-null-pointer-checks -Wnull-dereference" } */
 /* { dg-skip-if "" keeps_null_pointer_checks } */
 
 
@@ -39,8 +38,8 @@ d_type (struct d_info *di)
 {
    struct demangle_component *ret;
    ret = d_make_empty (di);
-   ret->type = 42;
-   ret->zzz = -1;
+   ret->type = 42;             /* { dg-warning "null pointer dereference" } */
+   ret->zzz = -1;              /* { dg-warning "null pointer dereference" } */
    return ret;
 }
 
@@ -53,8 +52,3 @@ d_type (struct d_info *di)
 /* { dg-final { scan-tree-dump-times "->type = 42" 1 "isolate-paths"} } */
 /* { dg-final { scan-tree-dump-times "->type ={v} 0" 1 "isolate-paths"} } */
 /* { dg-final { scan-tree-dump-times "->zzz" 1 "isolate-paths"} } */
-
-
-
-
-
index b8838e1833c87c578fe8cb7f1e40e9fe5febfbc2..b993849e96d8afb1157330d199bab69b6d00dd40 100644 (file)
@@ -35,7 +35,7 @@ bar (void)
    from a PHI, the second with an explicit return 0 in the IL.
 
    We also verify that after isolation phi-cprop simplifies the
-   return statement so that it returns &z directly.
+   return statement so that it returns &z directly. */
 /* { dg-final { scan-tree-dump-times "__builtin_trap" 2 "isolate-paths"} } */
 /* { dg-final { scan-tree-dump-times "return &z;" 1 "phicprop1"} } */
 
index ae30a10636ecbbca9450a14bf84ff6818338b102..d8fd8361ead3bb1cc7dbb915981a34b25d249560 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do compile } */ 
-/* { dg-options "-O2 -fdump-tree-isolate-paths -fdelete-null-pointer-checks" } */
+/* { dg-options "-O2 -fdump-tree-isolate-paths -fdelete-null-pointer-checks -Wnull-dereference" } */
 /* { dg-skip-if "" keeps_null_pointer_checks } */
 
 
@@ -29,7 +29,7 @@ static __inline__ void
 VEC_rtx_gc_safe_grow (VEC_rtx_gc ** vec_, int size_, const char *file_,
                       unsigned line_, const char *function_)
 {
-  ((*vec_) ? &(*vec_)->base : 0)->num = size_;
+  ((*vec_) ? &(*vec_)->base : 0)->num = size_; /* { dg-warning "null pointer dereference" } */
 } 
 
 static __inline__ void
index b76adf0a113ed3cc34e1471254607aa045c86f65..0a88d7d47f6e91d82f167c2ab0e926c58c1ef0de 100644 (file)
@@ -24,7 +24,7 @@ bar (void)
    a return statement.
 
    We also verify that after isolation phi-cprop simplifies the
-   return statement so that it returns &z directly.
+   return statement so that it returns &z directly. */
 /* { dg-final { scan-tree-dump-times "__builtin_trap" 2 "isolate-paths"} } */
 /* { dg-final { scan-tree-dump-times "foo .&z.;" 1 "phicprop1"} } */
 
index 7ae00d920d7ca20f0abecd7bca3c98dab0553976..f67e3dad86eef92ce3c626b60f7d3091e6d4081a 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do compile } */ 
-/* { dg-options "-O2 -fdelete-null-pointer-checks -fdump-tree-isolate-paths -fdump-tree-optimized" } */
+/* { dg-options "-O2 -fdelete-null-pointer-checks -fdump-tree-isolate-paths -fdump-tree-optimized -Wnull-dereference" } */
 /* { dg-skip-if "" keeps_null_pointer_checks } */
 
 struct demangle_component
@@ -36,8 +36,8 @@ d_type (struct d_info *di)
 {
    struct demangle_component *ret;
    ret = d_make_empty (di);
-   foo (ret->type);
-   bar (ret->zzz);
+   foo (ret->type); /* { dg-warning "null pointer dereference" } */
+   bar (ret->zzz); /* { dg-warning "null pointer dereference" } */
    return ret;
 }
 
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/wnull-dereference.c b/gcc/testsuite/gcc.dg/tree-ssa/wnull-dereference.c
new file mode 100644 (file)
index 0000000..db36acc
--- /dev/null
@@ -0,0 +1,82 @@
+/* { dg-do compile } */ 
+/* PR c/16531 */
+/* { dg-options "-O2 -fdelete-null-pointer-checks -Wnull-dereference" } */
+/* { dg-skip-if "" keeps_null_pointer_checks } */
+
+#ifndef __cplusplus
+#define NULL (void *)0
+#else
+#define NULL nullptr
+#endif
+
+struct t
+{
+  int bar;
+};
+
+struct t2
+{
+  struct t *s;
+};
+
+void test1 ()
+{
+  struct t *s = NULL;
+  s->bar = 1;  /* { dg-warning "null" } */
+}
+
+void test2 (struct t *s)
+{
+  if (s == NULL && s->bar > 2)  /* { dg-warning "null" } */
+    return;
+
+  s->bar = 3;
+}
+
+void test3 (struct t *s)
+{
+  if (s != NULL || s->bar > 2)  /* { dg-warning "null" } */
+    return;
+
+  s->bar = 3;  /* { dg-warning "null" } */
+}
+
+int test4 (struct t *s)
+{
+  if (s != NULL && s->bar > 2)  /* { dg-bogus "null" } */
+    return 1;
+  return 0;
+}
+
+int test5 (struct t *s)
+{
+  if (s == NULL || s->bar > 2)  /* { dg-bogus "null" } */
+    return 1;
+  return 0;
+}
+
+int test6 (struct t2 *s)
+{
+  if (s->s == 0 && s->s->bar == 0)  /* { dg-warning "null" } */
+    return 1;
+  return 0;
+}
+
+int test7 (struct t *s)
+{
+  s = 0;
+  return s->bar;  /* { dg-warning "null" } */
+}
+
+int test8 ()
+{
+  return ((struct t *)0)->bar;  /* { dg-warning "null" } */
+}
+
+void test9 (struct t **s)
+{
+  if (s == 0)
+    *s = 0;  /* { dg-warning "null" } */
+}
+
+
index d96268314c4177c6adc35d6c51e97432db66a3f2..31a9d21b4bad22d8377eae518c282ed8a521f37f 100644 (file)
@@ -4938,7 +4938,7 @@ infer_value_range (gimple stmt, tree op, enum tree_code *comp_code_p, tree *val_
        return false;
     }
 
-  if (infer_nonnull_range (stmt, op, true, true))
+  if (infer_nonnull_range (stmt, op))
     {
       *val_p = build_int_cst (TREE_TYPE (op), 0);
       *comp_code_p = NE_EXPR;
index d75c4ee0f3de8f2152cf2bbb172d19085db2bd92..7983c930c6610093a85354f4930a830f30579859 100644 (file)
@@ -1615,7 +1615,7 @@ instrument_nonnull_arg (gimple_stmt_iterator *gsi)
     {
       tree arg = gimple_call_arg (stmt, i);
       if (POINTER_TYPE_P (TREE_TYPE (arg))
-         && infer_nonnull_range (stmt, arg, false, true))
+         && infer_nonnull_range_by_attribute (stmt, arg))
        {
          gimple g;
          if (!is_gimple_val (arg))
@@ -1680,7 +1680,7 @@ instrument_nonnull_return (gimple_stmt_iterator *gsi)
   if (arg
       && POINTER_TYPE_P (TREE_TYPE (arg))
       && is_gimple_val (arg)
-      && infer_nonnull_range (stmt, arg, false, true))
+      && infer_nonnull_range_by_attribute (stmt, arg))
     {
       basic_block then_bb, fallthru_bb;
       *gsi = create_cond_insert_point (gsi, true, false, true,