From: Manuel López-Ibáñez Date: Wed, 5 Aug 2015 17:36:29 +0000 (+0000) Subject: re PR c/16351 (NULL dereference warnings) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=76787f70097bb232a9bb4afbd9e8c190e57291f0;p=gcc.git re PR c/16351 (NULL dereference warnings) gcc/ChangeLog: 2015-08-05 Manuel López-Ibáñez Jeff Law 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 Jeff Law 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 From-SVN: r226640 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 38c3fdc8947..c29e1c6e4da 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,21 @@ +2015-08-05 Manuel López-Ibáñez + Jeff Law + + 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 * gensupport.c (sequence_num): Replace with... diff --git a/gcc/common.opt b/gcc/common.opt index 8f25f8b35cf..fb9e9daa754 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -592,6 +592,10 @@ Wlarger-than= Common RejectNegative Joined UInteger Warning -Wlarger-than= Warn if an object is larger than 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. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index f7daa02d658..8f375b7e527 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -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 diff --git a/gcc/gimple-ssa-isolate-paths.c b/gcc/gimple-ssa-isolate-paths.c index 7d3758ce5ab..6f84f85856b 100644 --- a/gcc/gimple-ssa-isolate-paths.c +++ b/gcc/gimple-ssa-isolate-paths.c @@ -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 *) diff --git a/gcc/gimple.c b/gcc/gimple.c index 89291b07cf4..e31a2731727 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -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 (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 (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; } diff --git a/gcc/gimple.h b/gcc/gimple.h index 596d28d31d1..d4467b5a163 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -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); extern void preprocess_case_label_vec_for_gimple (vec, tree, tree *); extern void gimple_seq_set_location (gimple_seq, location_t); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index e81d1df5d5b..205975df36a 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2015-08-05 Manuel López-Ibáñez + Jeff Law + + 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 PR middle-end/66311 diff --git a/gcc/testsuite/gcc.dg/tree-ssa/isolate-1.c b/gcc/testsuite/gcc.dg/tree-ssa/isolate-1.c index 51ee328a088..486c307cbdc 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/isolate-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/isolate-1.c @@ -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"} } */ - - - - - diff --git a/gcc/testsuite/gcc.dg/tree-ssa/isolate-2.c b/gcc/testsuite/gcc.dg/tree-ssa/isolate-2.c index b8838e1833c..b993849e96d 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/isolate-2.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/isolate-2.c @@ -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"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/isolate-3.c b/gcc/testsuite/gcc.dg/tree-ssa/isolate-3.c index ae30a10636e..d8fd8361ead 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/isolate-3.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/isolate-3.c @@ -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 diff --git a/gcc/testsuite/gcc.dg/tree-ssa/isolate-4.c b/gcc/testsuite/gcc.dg/tree-ssa/isolate-4.c index b76adf0a113..0a88d7d47f6 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/isolate-4.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/isolate-4.c @@ -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"} } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/isolate-5.c b/gcc/testsuite/gcc.dg/tree-ssa/isolate-5.c index 7ae00d920d7..f67e3dad86e 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/isolate-5.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/isolate-5.c @@ -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 index 00000000000..db36acc15b7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/wnull-dereference.c @@ -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" } */ +} + + diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c index d96268314c4..31a9d21b4ba 100644 --- a/gcc/tree-vrp.c +++ b/gcc/tree-vrp.c @@ -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; diff --git a/gcc/ubsan.c b/gcc/ubsan.c index d75c4ee0f3d..7983c930c66 100644 --- a/gcc/ubsan.c +++ b/gcc/ubsan.c @@ -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,