This incremental patch brings the implementation of reference binding instrumentation, that is, it detects cases like
int *p = NULL; int &r = *p; or auto &&rr = *p; and similarly. It does so by adding a COMPOUND_EXPR to the decl. As standard says, "A reference shall be initialized to refer to a valid object or function.". Jason, is that tiny C++ part ok with you? Regtested/bootstrapped on x86_64-linux, ran bootstrap-ubsan, ok for trunk? 2013-11-18 Marek Polacek <pola...@redhat.com> c-family/ * c-ubsan.h (ubsan_instrument_reference): Declare. * c-ubsan.c (ubsan_instrument_reference): New function. cp/ * decl.c (cp_finish_decl): Instrument reference binding. testsuite/ * g++.dg/ubsan/null-1.C: New test. --- gcc/c-family/c-ubsan.h.mp2 2013-11-18 12:52:00.572671736 +0100 +++ gcc/c-family/c-ubsan.h 2013-11-18 12:52:25.751761970 +0100 @@ -24,5 +24,6 @@ along with GCC; see the file COPYING3. extern tree ubsan_instrument_division (location_t, tree, tree); extern tree ubsan_instrument_shift (location_t, enum tree_code, tree, tree); extern tree ubsan_instrument_vla (location_t, tree); +extern tree ubsan_instrument_reference (location_t, tree); #endif /* GCC_C_UBSAN_H */ --- gcc/c-family/c-ubsan.c.mp2 2013-11-18 12:49:00.553025438 +0100 +++ gcc/c-family/c-ubsan.c 2013-11-18 15:26:53.835120271 +0100 @@ -179,3 +179,30 @@ ubsan_instrument_vla (location_t loc, tr return t; } + +/* Instrument reference binding, that is, ensure that the reference + declaration doesn't bind the reference to a NULL pointer. */ + +tree +ubsan_instrument_reference (location_t loc, tree init) +{ + if (!INDIRECT_REF_P (init)) + /* This may happen, e.g. int &&r4 = p;, so don't put an assert here. */ + return init; + + init = TREE_OPERAND (init, 0); + tree eq_expr = fold_build2 (EQ_EXPR, boolean_type_node, init, + build_zero_cst (TREE_TYPE (init))); + const struct ubsan_mismatch_data m + = { build_zero_cst (pointer_sized_int_node), + build_int_cst (unsigned_char_type_node, UBSAN_REF_BINDING)}; + tree data = ubsan_create_data ("__ubsan_null_data", + loc, &m, + ubsan_type_descriptor (TREE_TYPE (init), + true), NULL_TREE); + data = build_fold_addr_expr_loc (loc, data); + tree fn = builtin_decl_implicit (BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH); + fn = build_call_expr_loc (loc, fn, 2, data, + build_zero_cst (pointer_sized_int_node)); + return fold_build3 (COND_EXPR, void_type_node, eq_expr, fn, void_zero_node); +} --- gcc/cp/decl.c.mp2 2013-11-15 17:14:24.887512640 +0100 +++ gcc/cp/decl.c 2013-11-18 13:38:15.356077696 +0100 @@ -6216,6 +6217,11 @@ cp_finish_decl (tree decl, tree init, bo if (decl_maybe_constant_var_p (decl)) TREE_CONSTANT (decl) = 1; } + if (flag_sanitize & SANITIZE_NULL + && TREE_CODE (type) == REFERENCE_TYPE) + init = fold_build2 (COMPOUND_EXPR, TREE_TYPE (init), + ubsan_instrument_reference (input_location, init), + init); } if (processing_template_decl) --- gcc/testsuite/g++.dg/ubsan/null-1.C.mp2 2013-11-18 15:29:44.722831910 +0100 +++ gcc/testsuite/g++.dg/ubsan/null-1.C 2013-11-18 15:29:54.744874505 +0100 @@ -0,0 +1,23 @@ +/* { dg-do run } */ +/* { dg-options "-fsanitize=null -w -std=c++11" } */ + +typedef const long int L; + +int +main (void) +{ + int *p = 0; + L *l = 0; + + int &r = *p; + auto &r2 = *p; + L &lr = *l; + + /* Try an rvalue reference. */ + auto &&r3 = *p; +} + +/* { dg-output "reference binding to null pointer of type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*reference binding to null pointer of type 'const L'(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*reference binding to null pointer of type 'int'(\n|\r\n|\r)" } */ Marek