diff mbox

Add reference binding instrumentation

Message ID 20131118163951.GD30062@redhat.com
State New
Headers show

Commit Message

Marek Polacek Nov. 18, 2013, 4:39 p.m. UTC
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  <polacek@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.


	Marek

Comments

Jason Merrill Nov. 19, 2013, 3:07 p.m. UTC | #1
On 11/18/2013 11:39 AM, Marek Polacek wrote:
> +	init = fold_build2 (COMPOUND_EXPR, TREE_TYPE (init),
> +			    ubsan_instrument_reference (input_location, init),
> +			    init);

This looks like it will evaluate init twice.

Jason
diff mbox

Patch

--- 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)" } */