diff mbox

Implement -fsanitize=null + new sanopt pass

Message ID 20131112231348.GU30062@redhat.com
State New
Headers show

Commit Message

Marek Polacek Nov. 12, 2013, 11:13 p.m. UTC
Hi!

Lately I've been working on the ubsan NULL pointer checking.  Its
purpose is to detect load from/store to NULL pointer.  So, e.g.,

  int *p = NULL;
  struct S *s = NULL;
  
  if (*p) { ... }
  x = struct s->i;

etc.

I should explain a bit here about the implementation of it.  Firstly,
I wanted to implement it in the FEs, but that proved impossible,
or at least too hard: there is no sure-fire how to distinguish
between loads and stores from/to pointers, while on GIMPLE,
walk_gimple_op will compute is_lhs for us.  So I've resorted to
make use of that.

There are two new passes: ubsan and sanopt.  The ubsan pass is run
very early, and its purpose is merely to insert the UBSAN_NULL internal
function calls before load/stores, that is:

  p_1 = 0B; 
  UBSAN_NULL (p_1, 0); 
  _3 = *p_1;

The first argument of the UBSAN_NULL is the pointer itself; the second
argument is just a value that says whether it is actually a store, a
load, a member access, etc.

Now, the second new pass, sanopt, is run very late.  This pass is 
implemented in asan.c and is not designed only for ubsan, we will use
it for asan/tsan/whatever_might_crop_up_in_the_future as well.  The idea
behind the sanopt pass is simple: walk the statements and expand the
UBSAN_NULL calls.  What we expand this call to is roughly the
following:

  if (p_1 == 0B) 
    goto <bb 4>; 
  else
    goto <bb 3>; 

  <bb 4>: 
  __builtin___ubsan_handle_type_mismatch (&*.Lubsan_data0, 0); 

  <bb 3>: 
  _2 = *p_1;

(With optimizations on this will look slightly different, as CCP does its job,
etc.)
The fact that we're always splitting the BBs, thus this might be
rather expensive on large TUs, is plain to see.  But I think the
basic idea is sound.

The fact that in sanopt pass we're creating a new internal structure
proved to be particularly arduous, since on -O0, cgraph stuff refused
to actually emit this structure.  The problem was that
varpool_assemble_decl wasn't properly called when flag_toplevel_reorder
wasn't in effect.  Hence the cgraphunit.c hunk.  Honza, does it look sane to
you?

There are a few issues that are vying for our/my attention:

1) I'm getting an ICE with -flto in lto_output_edge, it doesn't like
   the fact that internal call is a leaf function.  So, I disabled
   ubsan tests with lto, since I don't readily see how to solve this.
2) bootstrap-ubsan almost passes, but the bootstrap fails when building
   all-fixincludes.  The problem here is that libiberty.a is built
   with -fsanitize=undefined, but fixincludes, when linking,
   don't link libubsan in.  My attemps to tweak
   FIXINC_CFLAGS/LDFLAGS/BOOT_LDFLAGS and whatnot weren't successfull.
3) Member call instrumentation (*) doesn't work with SRA turned on.
4) int &r = 0; sanitization is not implemented yet.  But this will have
   to be done in the C++ FE.

(*) By this I mean:

struct S { int f (void) { return 0; } };
struct S *s = NULL;
s->f();

Currently this will not fail, only the 'this' pointer will be bogus.

So, how does this look like?

Regtested/bootstrapped on x86_64-linux.

2013-11-12  Marek Polacek  <polacek@redhat.com>

config/
	* bootstrap-ubsan.mk (POSTSTAGE1_LDFLAGS): Add -ldl.
gcc/c-family/
	* c-ubsan.c (ubsan_instrument_division): Adjust ubsan_create_data
	call.
	(ubsan_instrument_shift): Likewise.
	(ubsan_instrument_vla): Likewise.
gcc/
	* opts.c (common_handle_option): Add -fsanitize=null option.
	Turn off -fdelete-null-pointer-checks option when doing the
	NULL pointer checking.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH): Add.
	* tree-pass.h (make_pass_ubsan): Declare.
	(make_pass_sanopt): Declare.
	* timevar.def (TV_TREE_UBSAN): New timevar.
	* passes.def: Add pass_sanopt and pass_ubsan.
	* ubsan.h (ubsan_null_ckind): New enum.
	(ubsan_mismatch_data): New struct.
	(ubsan_expand_null_ifn): Declare.
	(ubsan_create_data): Adjust declaration.
	(ubsan_type_descriptor): Likewise.
	* asan.c: Include "ubsan.h".
	(pass_data_sanopt): New pass.
	(execute_sanopt): New function.
	(gate_sanopt): Likewise.
	(make_pass_sanopt): Likewise.
	(class pass_sanopt): New class.
	* ubsan.c: Include "tree-pass.h", "gimple-ssa.h" and "cfgloop.h". 
	(PROB_VERY_UNLIKELY): Define.
	(tree_type_map_hash): New function.
	(ubsan_type_descriptor): Add new parameter.
	Improve type name generation.
	(ubsan_create_data): Add new parameter.  Add pointer data into
	ubsan structure.
	(ubsan_expand_null_ifn): New function.
	(instrument_member_call): Likewise.
	(instrument_mem_ref): Likewise.
	(instrument_null): Likewise.
	(ubsan_pass): Likewise.
	(gate_ubsan): Likewise.
	(make_pass_ubsan): Likewise.
	(ubsan_instrument_unreachable): Adjust ubsan_create_data call.
	(class pass_ubsan): New class.
	(pass_data_ubsan): New pass.
	* flag-types.h (enum sanitize_code): Add SANITIZE_NULL.
	* internal-fn.c (expand_UBSAN_NULL): New function.
	* cgraphunit.c (varpool_finalize_decl): Call varpool_assemble_decl
	even when !flag_toplevel_reorder.
	* internal-fn.def (UBSAN_NULL): New.
gcc/testsuite/
	* c-c++-common/ubsan/null-1.c: New test.
	* c-c++-common/ubsan/null-2.c: New test.
	* c-c++-common/ubsan/null-3.c: New test.
	* c-c++-common/ubsan/null-4.c: New test.
	* c-c++-common/ubsan/null-5.c: New test.
	* c-c++-common/ubsan/null-6.c: New test.
	* c-c++-common/ubsan/null-7.c: New test.
	* c-c++-common/ubsan/null-8.c: New test.
	* c-c++-common/ubsan/null-9.c: New test.
	* c-c++-common/ubsan/null-10.c: New test.
	* c-c++-common/ubsan/null-11.c: New test.
	* gcc.dg/ubsan/c99-shift-2.c: Adjust dg-output.
	* c-c++-common/ubsan/shift-1.c: Likewise.
	* c-c++-common/ubsan/div-by-zero-3.c: Likewise.


	Marek

Comments

Jakub Jelinek Nov. 18, 2013, 1:51 p.m. UTC | #1
On Wed, Nov 13, 2013 at 12:13:48AM +0100, Marek Polacek wrote:
> --- gcc/config/bootstrap-ubsan.mk.mp	2013-11-12 13:46:13.345182065 +0100
> +++ gcc/config/bootstrap-ubsan.mk	2013-11-12 13:46:49.812314016 +0100
> @@ -2,6 +2,6 @@
>  
>  STAGE2_CFLAGS += -fsanitize=undefined
>  STAGE3_CFLAGS += -fsanitize=undefined
> -POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread \
> +POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread -ldl \

Hopefully with my pending patch you can remove the -lpthread -ldl again, but
ok for now.
> +      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
> +	{
> +	  gimple stmt = gsi_stmt (gsi);
> +
> +	  if (gimple_code (stmt) != GIMPLE_CALL)

if (is_gimple_call (stmt))

Ok with those changes.

	Jakub
Jakub Jelinek Nov. 18, 2013, 1:52 p.m. UTC | #2
On Mon, Nov 18, 2013 at 02:51:41PM +0100, Jakub Jelinek wrote:
> On Wed, Nov 13, 2013 at 12:13:48AM +0100, Marek Polacek wrote:
> > --- gcc/config/bootstrap-ubsan.mk.mp	2013-11-12 13:46:13.345182065 +0100
> > +++ gcc/config/bootstrap-ubsan.mk	2013-11-12 13:46:49.812314016 +0100
> > @@ -2,6 +2,6 @@
> >  
> >  STAGE2_CFLAGS += -fsanitize=undefined
> >  STAGE3_CFLAGS += -fsanitize=undefined
> > -POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread \
> > +POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread -ldl \
> 
> Hopefully with my pending patch you can remove the -lpthread -ldl again, but
> ok for now.
> > +      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
> > +	{
> > +	  gimple stmt = gsi_stmt (gsi);
> > +
> > +	  if (gimple_code (stmt) != GIMPLE_CALL)
> 
> if (is_gimple_call (stmt))
> 
> Ok with those changes.

Oh, one more thing, please update gcc/doc/, the -fsanitize= description is
far from up to date there.

	Jakub
Marek Polacek Nov. 18, 2013, 2:44 p.m. UTC | #3
On Mon, Nov 18, 2013 at 02:51:41PM +0100, Jakub Jelinek wrote:
> On Wed, Nov 13, 2013 at 12:13:48AM +0100, Marek Polacek wrote:
> > --- gcc/config/bootstrap-ubsan.mk.mp	2013-11-12 13:46:13.345182065 +0100
> > +++ gcc/config/bootstrap-ubsan.mk	2013-11-12 13:46:49.812314016 +0100
> > @@ -2,6 +2,6 @@
> >  
> >  STAGE2_CFLAGS += -fsanitize=undefined
> >  STAGE3_CFLAGS += -fsanitize=undefined
> > -POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread \
> > +POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread -ldl \
> 
> Hopefully with my pending patch you can remove the -lpthread -ldl again, but
> ok for now.

Cool.

> > +      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
> > +	{
> > +	  gimple stmt = gsi_stmt (gsi);
> > +
> > +	  if (gimple_code (stmt) != GIMPLE_CALL)
> 
> if (is_gimple_call (stmt))

Fixed.
 
> Ok with those changes.

Thanks.  Also I'll have to add some headers after the gimple.h reorg,
but that is an obvious change.

	Marek
H.J. Lu Nov. 29, 2013, 7:22 p.m. UTC | #4
On Mon, Nov 18, 2013 at 6:44 AM, Marek Polacek <polacek@redhat.com> wrote:
> On Mon, Nov 18, 2013 at 02:51:41PM +0100, Jakub Jelinek wrote:
>> On Wed, Nov 13, 2013 at 12:13:48AM +0100, Marek Polacek wrote:
>> > --- gcc/config/bootstrap-ubsan.mk.mp        2013-11-12 13:46:13.345182065 +0100
>> > +++ gcc/config/bootstrap-ubsan.mk   2013-11-12 13:46:49.812314016 +0100
>> > @@ -2,6 +2,6 @@
>> >
>> >  STAGE2_CFLAGS += -fsanitize=undefined
>> >  STAGE3_CFLAGS += -fsanitize=undefined
>> > -POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread \
>> > +POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread -ldl \
>>
>> Hopefully with my pending patch you can remove the -lpthread -ldl again, but
>> ok for now.
>

You shouldn't use -ldl directly.  Not all OSes have libdl.  You
should extract the libdl check from gcc/configure.ac and
set LIBDL instead by changing gcc/Makefile.in

PLUGINLIBS = @pluginlibs@

to

LIBDL = @libdl@
PLUGINLIBS = @pluginlibs@ $(LIBD)

Then you can use

POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread $(LIBDL) \
Jakub Jelinek Nov. 29, 2013, 7:32 p.m. UTC | #5
On Fri, Nov 29, 2013 at 11:22:00AM -0800, H.J. Lu wrote:
> On Mon, Nov 18, 2013 at 6:44 AM, Marek Polacek <polacek@redhat.com> wrote:
> > On Mon, Nov 18, 2013 at 02:51:41PM +0100, Jakub Jelinek wrote:
> >> On Wed, Nov 13, 2013 at 12:13:48AM +0100, Marek Polacek wrote:
> >> > --- gcc/config/bootstrap-ubsan.mk.mp        2013-11-12 13:46:13.345182065 +0100
> >> > +++ gcc/config/bootstrap-ubsan.mk   2013-11-12 13:46:49.812314016 +0100
> >> > @@ -2,6 +2,6 @@
> >> >
> >> >  STAGE2_CFLAGS += -fsanitize=undefined
> >> >  STAGE3_CFLAGS += -fsanitize=undefined
> >> > -POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread \
> >> > +POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread -ldl \
> >>
> >> Hopefully with my pending patch you can remove the -lpthread -ldl again, but
> >> ok for now.
> >
> 
> You shouldn't use -ldl directly.  Not all OSes have libdl.  You
> should extract the libdl check from gcc/configure.ac and
> set LIBDL instead by changing gcc/Makefile.in

-static-libubsan should add all the libraries needed of libubsan.a by now,
so -lpthread -ldl should be just removed from POSTSTAGE1_LDFLAGS.

	Jakub
diff mbox

Patch

--- gcc/config/bootstrap-ubsan.mk.mp	2013-11-12 13:46:13.345182065 +0100
+++ gcc/config/bootstrap-ubsan.mk	2013-11-12 13:46:49.812314016 +0100
@@ -2,6 +2,6 @@ 
 
 STAGE2_CFLAGS += -fsanitize=undefined
 STAGE3_CFLAGS += -fsanitize=undefined
-POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread \
+POSTSTAGE1_LDFLAGS += -fsanitize=undefined -static-libubsan -lpthread -ldl \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ubsan/ \
 		      -B$$r/prev-$(TARGET_SUBDIR)/libsanitizer/ubsan/.libs
--- gcc/gcc/opts.c.mp	2013-11-12 13:46:13.354182104 +0100
+++ gcc/gcc/opts.c	2013-11-12 13:46:49.824314069 +0100
@@ -1446,6 +1446,7 @@  common_handle_option (struct gcc_options
 	      { "unreachable", SANITIZE_UNREACHABLE,
 		sizeof "unreachable" - 1 },
 	      { "vla-bound", SANITIZE_VLA, sizeof "vla-bound" - 1 },
+	      { "null", SANITIZE_NULL, sizeof "null" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
@@ -1487,6 +1488,10 @@  common_handle_option (struct gcc_options
 	    p = comma + 1;
 	  }
 
+	/* When instrumenting the pointers, we don't want to remove
+	   the null pointer checks.  */
+	if (flag_sanitize & SANITIZE_NULL)
+	  opts->x_flag_delete_null_pointer_checks = 0;
 	break;
       }
 
--- gcc/gcc/c-family/c-ubsan.c.mp	2013-11-12 13:46:13.348182077 +0100
+++ gcc/gcc/c-family/c-ubsan.c	2013-11-12 13:46:49.819314047 +0100
@@ -74,7 +74,8 @@  ubsan_instrument_division (location_t lo
      make sure it gets evaluated before the condition.  */
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   tree data = ubsan_create_data ("__ubsan_overflow_data",
-				 loc, ubsan_type_descriptor (type),
+				 loc, NULL,
+				 ubsan_type_descriptor (type, false),
 				 NULL_TREE);
   data = build_fold_addr_expr_loc (loc, data);
   tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_DIVREM_OVERFLOW);
@@ -142,8 +143,10 @@  ubsan_instrument_shift (location_t loc,
      make sure it gets evaluated before the condition.  */
   t = fold_build2 (COMPOUND_EXPR, TREE_TYPE (t), op0, t);
   tree data = ubsan_create_data ("__ubsan_shift_data",
-				 loc, ubsan_type_descriptor (type0),
-				 ubsan_type_descriptor (type1), NULL_TREE);
+				 loc, NULL,
+				 ubsan_type_descriptor (type0, false),
+				 ubsan_type_descriptor (type1, false),
+				 NULL_TREE);
 
   data = build_fold_addr_expr_loc (loc, data);
 
@@ -167,7 +170,9 @@  ubsan_instrument_vla (location_t loc, tr
 
   t = fold_build2 (LE_EXPR, boolean_type_node, size, build_int_cst (type, 0));
   tree data = ubsan_create_data ("__ubsan_vla_data",
-				 loc, ubsan_type_descriptor (type), NULL_TREE);
+				 loc, NULL,
+				 ubsan_type_descriptor (type, false),
+				 NULL_TREE);
   data = build_fold_addr_expr_loc (loc, data);
   tt = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_VLA_BOUND_NOT_POSITIVE);
   tt = build_call_expr_loc (loc, tt, 2, data, ubsan_encode_value (size));
--- gcc/gcc/sanitizer.def.mp	2013-11-12 13:46:13.356182114 +0100
+++ gcc/gcc/sanitizer.def	2013-11-12 13:46:49.825314074 +0100
@@ -301,3 +301,7 @@  DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HAN
 		      "__ubsan_handle_vla_bound_not_positive",
 		      BT_FN_VOID_PTR_PTR,
 		      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH,
+		      "__ubsan_handle_type_mismatch",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
--- gcc/gcc/tree-pass.h.mp	2013-11-12 13:46:13.362182138 +0100
+++ gcc/gcc/tree-pass.h	2013-11-12 13:46:49.829314091 +0100
@@ -447,6 +447,8 @@  extern gimple_opt_pass *make_pass_split_
 extern gimple_opt_pass *make_pass_feedback_split_functions (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_strength_reduction (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_vtable_verify (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_ubsan (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_sanopt (gcc::context *ctxt);
 
 /* IPA Passes */
 extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context *ctxt);
--- gcc/gcc/ubsan.h.mp	2013-11-12 13:46:13.364182146 +0100
+++ gcc/gcc/ubsan.h	2013-11-12 22:28:30.918518781 +0100
@@ -21,9 +21,26 @@  along with GCC; see the file COPYING3.
 #ifndef GCC_UBSAN_H
 #define GCC_UBSAN_H
 
+/* The various kinds of NULL pointer checks.  */
+enum ubsan_null_ckind {
+  UBSAN_LOAD_OF,
+  UBSAN_STORE_OF,
+  UBSAN_REF_BINDING,
+  UBSAN_MEMBER_ACCESS,
+  UBSAN_MEMBER_CALL
+};
+
+/* An extra data used by ubsan pointer checking.  */
+struct ubsan_mismatch_data {
+  tree align;
+  tree ckind;
+};
+
+extern void ubsan_expand_null_ifn (gimple_stmt_iterator);
 extern tree ubsan_instrument_unreachable (location_t);
-extern tree ubsan_create_data (const char *, location_t, ...);
-extern tree ubsan_type_descriptor (tree);
+extern tree ubsan_create_data (const char *, location_t,
+			       const struct ubsan_mismatch_data *, ...);
+extern tree ubsan_type_descriptor (tree, bool);
 extern tree ubsan_encode_value (tree);
 extern bool is_ubsan_builtin_p (tree);
 
--- gcc/gcc/ubsan.c.mp	2013-11-12 13:46:13.363182142 +0100
+++ gcc/gcc/ubsan.c	2013-11-12 22:35:11.362129449 +0100
@@ -23,15 +23,21 @@  along with GCC; see the file COPYING3.
 #include "coretypes.h"
 #include "tree.h"
 #include "cgraph.h"
+#include "tree-pass.h"
 #include "gimple.h"
+#include "gimple-ssa.h"
 #include "hashtab.h"
 #include "pointer-set.h"
 #include "output.h"
 #include "tm_p.h"
 #include "toplev.h"
+#include "cfgloop.h"
 #include "ubsan.h"
 #include "c-family/c-common.h"
 
+/* From trans-mem.c.  */
+#define PROB_VERY_UNLIKELY      (REG_BR_PROB_BASE / 2000 - 1)
+
 /* Map from a tree to a VAR_DECL tree.  */
 
 struct GTY(()) tree_type_map {
@@ -40,9 +46,16 @@  struct GTY(()) tree_type_map {
 };
 
 #define tree_type_map_eq tree_map_base_eq
-#define tree_type_map_hash tree_map_base_hash
 #define tree_type_map_marked_p tree_map_base_marked_p
 
+/* Hash from a tree in a tree_type_map.  */
+
+unsigned int
+tree_type_map_hash (const void *item)
+{
+  return TYPE_UID (((const struct tree_type_map *)item)->type.from);
+}
+
 static GTY ((if_marked ("tree_type_map_marked_p"), param_is (struct tree_type_map)))
      htab_t decl_tree_for_type;
 
@@ -240,12 +253,14 @@  get_ubsan_type_info_for_type (tree type)
 }
 
 /* Helper routine that returns ADDR_EXPR of a VAR_DECL of a type
-   descriptor.  It first looks into the pointer map; if not found,
-   create the VAR_DECL, put it into the pointer map and return the
-   ADDR_EXPR of it.  TYPE describes a particular type.  */
+   descriptor.  It first looks into the hash table; if not found,
+   create the VAR_DECL, put it into the hash table and return the
+   ADDR_EXPR of it.  TYPE describes a particular type.  WANT_POINTER_TYPE_P
+   means whether we are interested in the pointer type and not the pointer
+   itself.  */
 
 tree
-ubsan_type_descriptor (tree type)
+ubsan_type_descriptor (tree type, bool want_pointer_type_p)
 {
   /* See through any typedefs.  */
   type = TYPE_MAIN_VARIANT (type);
@@ -255,33 +270,73 @@  ubsan_type_descriptor (tree type)
     return decl;
 
   tree dtype = ubsan_type_descriptor_type ();
-  const char *tname;
+  tree type2 = type;
+  const char *tname = NULL;
+  char *pretty_name;
+  unsigned char deref_depth = 0;
   unsigned short tkind, tinfo;
 
-  /* At least for INTEGER_TYPE/REAL_TYPE/COMPLEX_TYPE, this should work.
-     For e.g. type_unsigned_for (type) or bit-fields, the TYPE_NAME
-     would be NULL.  */
-  if (TYPE_NAME (type) != NULL)
+  /* Get the name of the type, or the name of the pointer type.  */
+  if (want_pointer_type_p)
     {
-      if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE)
-	tname = IDENTIFIER_POINTER (TYPE_NAME (type));
+      gcc_assert (POINTER_TYPE_P (type));
+      type2 = TREE_TYPE (type);
+
+      /* Remove any '*' operators from TYPE.  */
+      while (POINTER_TYPE_P (type2))
+        deref_depth++, type2 = TREE_TYPE (type2);
+
+      if (TREE_CODE (type2) == METHOD_TYPE)
+        type2 = TYPE_METHOD_BASETYPE (type2);
+    }
+
+  if (TYPE_NAME (type2) != NULL)
+    {
+      if (TREE_CODE (TYPE_NAME (type2)) == IDENTIFIER_NODE)
+	tname = IDENTIFIER_POINTER (TYPE_NAME (type2));
       else
-	tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type)));
+	tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type2)));
     }
-  else
+
+  if (tname == NULL)
+    /* We weren't able to determine the type name.  */
     tname = "<unknown>";
 
-  if (TREE_CODE (type) == INTEGER_TYPE)
+  /* Decorate the type name with '', '*', "struct", or "union".  */
+  pretty_name = (char *) alloca (strlen (tname) + 16 + deref_depth);
+  if (want_pointer_type_p)
     {
-      /* For INTEGER_TYPE, this is 0x0000.  */
-      tkind = 0x000;
-      tinfo = get_ubsan_type_info_for_type (type);
-    }
-  else if (TREE_CODE (type) == REAL_TYPE)
-    /* We don't have float support yet.  */
-    gcc_unreachable ();
+      int pos = sprintf (pretty_name, "'%s%s%s%s%s%s%s",
+			 TYPE_VOLATILE (type2) ? "volatile " : "",
+			 TYPE_READONLY (type2) ? "const " : "",
+			 TYPE_RESTRICT (type2) ? "restrict " : "",
+			 TYPE_ATOMIC (type2) ? "_Atomic " : "",
+			 TREE_CODE (type2) == RECORD_TYPE
+			 ? "struct "
+			 : TREE_CODE (type2) == UNION_TYPE
+			   ? "union " : "", tname,
+			 deref_depth == 0 ? "" : " ");
+      while (deref_depth-- > 0)
+        pretty_name[pos++] = '*';
+      pretty_name[pos++] = '\'';
+      pretty_name[pos] = '\0';
+    }
   else
-    gcc_unreachable ();
+    sprintf (pretty_name, "'%s'", tname);
+
+  switch (TREE_CODE (type))
+    {
+    case INTEGER_TYPE:
+      tkind = 0x0000;
+      break;
+    case REAL_TYPE:
+      tkind = 0x0001;
+      break;
+    default:
+      tkind = 0xffff;
+      break;
+    }
+  tinfo = get_ubsan_type_info_for_type (type);
 
   /* Create a new VAR_DECL of type descriptor.  */
   char tmp_name[32];
@@ -295,8 +350,8 @@  ubsan_type_descriptor (tree type)
   DECL_IGNORED_P (decl) = 1;
   DECL_EXTERNAL (decl) = 0;
 
-  size_t len = strlen (tname);
-  tree str = build_string (len + 1, tname);
+  size_t len = strlen (pretty_name);
+  tree str = build_string (len + 1, pretty_name);
   TREE_TYPE (str) = build_array_type (char_type_node,
 				      build_index_type (size_int (len)));
   TREE_READONLY (str) = 1;
@@ -311,7 +366,7 @@  ubsan_type_descriptor (tree type)
   DECL_INITIAL (decl) = ctor;
   rest_of_decl_compilation (decl, 1, 0);
 
-  /* Save the address of the VAR_DECL into the pointer map.  */
+  /* Save the address of the VAR_DECL into the hash table.  */
   decl = build_fold_addr_expr (decl);
   decl_for_type_insert (type, decl);
 
@@ -320,10 +375,12 @@  ubsan_type_descriptor (tree type)
 
 /* Create a structure for the ubsan library.  NAME is a name of the new
    structure.  The arguments in ... are of __ubsan_type_descriptor type
-   and there are at most two of them.  */
+   and there are at most two of them.  MISMATCH are data used by ubsan
+   pointer checking.  */
 
 tree
-ubsan_create_data (const char *name, location_t loc, ...)
+ubsan_create_data (const char *name, location_t loc,
+		   const struct ubsan_mismatch_data *mismatch, ...)
 {
   va_list args;
   tree ret, t;
@@ -346,12 +403,12 @@  ubsan_create_data (const char *name, loc
       i++;
     }
 
-  va_start (args, loc);
+  va_start (args, mismatch);
   for (t = va_arg (args, tree); t != NULL_TREE;
        i++, t = va_arg (args, tree))
     {
       gcc_checking_assert (i < 3);
-      /* Save the tree argument for later use.  */
+      /* Save the tree arguments for later use.  */
       vec_safe_push (saved_args, t);
       fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
 			      td_type);
@@ -359,10 +416,27 @@  ubsan_create_data (const char *name, loc
       if (i)
 	DECL_CHAIN (fields[i - 1]) = fields[i];
     }
+  va_end (args);
+
+  if (mismatch != NULL)
+    {
+      /* We have to add two more decls.  */
+      fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
+				pointer_sized_int_node);
+      DECL_CONTEXT (fields[i]) = ret;
+      DECL_CHAIN (fields[i - 1]) = fields[i];
+      i++;
+
+      fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE,
+			      unsigned_char_type_node);
+      DECL_CONTEXT (fields[i]) = ret;
+      DECL_CHAIN (fields[i - 1]) = fields[i];
+      i++;
+    }
+
   TYPE_FIELDS (ret) = fields[0];
   TYPE_NAME (ret) = get_identifier (name);
   layout_type (ret);
-  va_end (args);
 
   /* Now, fill in the type.  */
   char tmp_name[32];
@@ -391,6 +465,13 @@  ubsan_create_data (const char *name, loc
       CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, t);
     }
 
+  if (mismatch != NULL)
+    {
+      /* Append the pointer data.  */
+      CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, mismatch->align);
+      CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, mismatch->ckind);
+    }
+
   TREE_CONSTANT (ctor) = 1;
   TREE_STATIC (ctor) = 1;
   DECL_INITIAL (var) = ctor;
@@ -405,7 +486,8 @@  ubsan_create_data (const char *name, loc
 tree
 ubsan_instrument_unreachable (location_t loc)
 {
-  tree data = ubsan_create_data ("__ubsan_unreachable_data", loc, NULL_TREE);
+  tree data = ubsan_create_data ("__ubsan_unreachable_data", loc, NULL,
+				 NULL_TREE);
   tree t = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_BUILTIN_UNREACHABLE);
   return build_call_expr_loc (loc, t, 1, build_fold_addr_expr_loc (loc, data));
 }
@@ -420,4 +502,199 @@  is_ubsan_builtin_p (tree t)
 		  "__builtin___ubsan_", 18) == 0;
 }
 
+/* Expand UBSAN_NULL internal call.  */
+
+void
+ubsan_expand_null_ifn (gimple_stmt_iterator gsi)
+{
+  gimple stmt = gsi_stmt (gsi);
+  location_t loc = gimple_location (stmt);
+  gcc_assert (gimple_call_num_args (stmt) == 2);
+  tree ptr = gimple_call_arg (stmt, 0);
+  tree ckind = gimple_call_arg (stmt, 1);
+
+  basic_block cur_bb = gsi_bb (gsi);
+
+  /* Split the original block holding the pointer dereference.  */
+  edge e = split_block (cur_bb, stmt);
+
+  /* Get a hold on the 'condition block', the 'then block' and the
+     'else block'.  */
+  basic_block cond_bb = e->src;
+  basic_block fallthru_bb = e->dest;
+  basic_block then_bb = create_empty_bb (cond_bb);
+  if (current_loops)
+    {
+      add_bb_to_loop (then_bb, cond_bb->loop_father);
+      loops_state_set (LOOPS_NEED_FIXUP);
+    }
+
+  /* Make an edge coming from the 'cond block' into the 'then block';
+     this edge is unlikely taken, so set up the probability accordingly.  */
+  e = make_edge (cond_bb, then_bb, EDGE_TRUE_VALUE);
+  e->probability = PROB_VERY_UNLIKELY;
+
+  /* Connect 'then block' with the 'else block'.  This is needed
+     as the ubsan routines we call in the 'then block' are not noreturn.
+     The 'then block' only has one outcoming edge.  */
+  make_single_succ_edge (then_bb, fallthru_bb, EDGE_FALLTHRU);
+
+  /* Set up the fallthrough basic block.  */
+  e = find_edge (cond_bb, fallthru_bb);
+  e->flags = EDGE_FALSE_VALUE;
+  e->count = cond_bb->count;
+  e->probability = REG_BR_PROB_BASE - PROB_VERY_UNLIKELY;
+
+  /* Update dominance info for the newly created then_bb; note that
+     fallthru_bb's dominance info has already been updated by
+     split_bock.  */
+  if (dom_info_available_p (CDI_DOMINATORS))
+    set_immediate_dominator (CDI_DOMINATORS, then_bb, cond_bb);
+
+  /* Put the ubsan builtin call into the newly created BB.  */
+  tree fn = builtin_decl_implicit (BUILT_IN_UBSAN_HANDLE_TYPE_MISMATCH);
+  const struct ubsan_mismatch_data m
+    = { build_zero_cst (pointer_sized_int_node), ckind };
+  tree data = ubsan_create_data ("__ubsan_null_data",
+				 loc, &m,
+				 ubsan_type_descriptor (TREE_TYPE (ptr), true),
+				 NULL_TREE);
+  data = build_fold_addr_expr_loc (loc, data);
+  gimple g = gimple_build_call (fn, 2, data,
+				build_zero_cst (pointer_sized_int_node));
+  gimple_set_location (g, loc);
+  gimple_stmt_iterator gsi2 = gsi_start_bb (then_bb);
+  gsi_insert_after (&gsi2, g, GSI_NEW_STMT);
+
+  /* Unlink the UBSAN_NULLs vops before replacing it.  */
+  unlink_stmt_vdef (stmt);
+
+  g = gimple_build_cond (EQ_EXPR, ptr, build_int_cst (TREE_TYPE (ptr), 0),
+			 NULL_TREE, NULL_TREE);
+  gimple_set_location (g, loc);
+
+  /* Replace the UBSAN_NULL with a GIMPLE_COND stmt.  */
+  gsi_replace (&gsi, g, false);
+}
+
+/* Instrument a member call.  We check whether 'this' is NULL.  */
+
+static void
+instrument_member_call (gimple_stmt_iterator *iter)
+{
+  tree this_parm = gimple_call_arg (gsi_stmt (*iter), 0);
+  tree kind = build_int_cst (unsigned_char_type_node, UBSAN_MEMBER_CALL);
+  gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, this_parm, kind);
+  gimple_set_location (g, gimple_location (gsi_stmt (*iter)));
+  gsi_insert_before (iter, g, GSI_SAME_STMT);
+}
+
+/* Instrument a memory reference.  T is the pointer, IS_LHS says
+   whether the pointer is on the left hand side of the assignment.  */
+
+static void
+instrument_mem_ref (tree t, gimple_stmt_iterator *iter, bool is_lhs)
+{
+  enum ubsan_null_ckind ikind = is_lhs ? UBSAN_STORE_OF : UBSAN_LOAD_OF;
+  if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_TYPE (t))))
+    ikind = UBSAN_MEMBER_ACCESS;
+  tree kind = build_int_cst (unsigned_char_type_node, ikind);
+  gimple g = gimple_build_call_internal (IFN_UBSAN_NULL, 2, t, kind);
+  gimple_set_location (g, gimple_location (gsi_stmt (*iter)));
+  gsi_insert_before (iter, g, GSI_SAME_STMT);
+}
+
+/* Callback function for the pointer instrumentation.  */
+
+static tree
+instrument_null (tree *tp, int * /*walk_subtree*/, void *data)
+{
+  tree t = *tp;
+  const enum tree_code code = TREE_CODE (t);
+  struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
+
+  if (code == MEM_REF
+      && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME)
+    instrument_mem_ref (TREE_OPERAND (t, 0), &wi->gsi, wi->is_lhs);
+  else if (code == ADDR_EXPR
+	   && POINTER_TYPE_P (TREE_TYPE (t))
+	   && TREE_CODE (TREE_TYPE (TREE_TYPE (t))) == METHOD_TYPE)
+    instrument_member_call (&wi->gsi);
+
+  return NULL_TREE;
+}
+
+/* Gate and execute functions for ubsan pass.  */
+
+static unsigned int
+ubsan_pass (void)
+{
+  basic_block bb;
+  gimple_stmt_iterator gsi;
+
+  FOR_EACH_BB (bb)
+    {
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
+	{
+	  struct walk_stmt_info wi;
+	  gimple stmt = gsi_stmt (gsi);
+	  if (is_gimple_debug (stmt))
+	    {
+	      gsi_next (&gsi);
+	      continue;
+	    }
+
+	  memset (&wi, 0, sizeof (wi));
+	  wi.gsi = gsi;
+	  walk_gimple_op (stmt, instrument_null, &wi);
+	  gsi_next (&gsi);
+	}
+    }
+  return 0;
+}
+
+static bool
+gate_ubsan (void)
+{
+  return flag_sanitize & SANITIZE_NULL;
+}
+
+namespace {
+
+const pass_data pass_data_ubsan =
+{
+  GIMPLE_PASS, /* type */
+  "ubsan", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_TREE_UBSAN, /* tv_id */
+  ( PROP_cfg | PROP_ssa ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_update_ssa, /* todo_flags_finish */
+};
+
+class pass_ubsan : public gimple_opt_pass
+{
+public:
+  pass_ubsan (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_ubsan, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_ubsan (); }
+  unsigned int execute () { return ubsan_pass (); }
+
+}; // class pass_ubsan
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_ubsan (gcc::context *ctxt)
+{
+  return new pass_ubsan (ctxt);
+}
+
 #include "gt-ubsan.h"
--- gcc/gcc/flag-types.h.mp	2013-11-12 13:46:13.350182085 +0100
+++ gcc/gcc/flag-types.h	2013-11-12 13:46:49.821314055 +0100
@@ -211,8 +211,9 @@  enum sanitize_code {
   SANITIZE_DIVIDE = 1 << 3,
   SANITIZE_UNREACHABLE = 1 << 4,
   SANITIZE_VLA = 1 << 5,
+  SANITIZE_NULL = 1 << 6,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
-		       | SANITIZE_VLA
+		       | SANITIZE_VLA | SANITIZE_NULL
 };
 
 /* flag_vtable_verify initialization levels. */
--- gcc/gcc/internal-fn.c.mp	2013-11-12 13:46:13.351182089 +0100
+++ gcc/gcc/internal-fn.c	2013-11-12 13:46:49.822314059 +0100
@@ -139,6 +139,14 @@  expand_GOMP_SIMD_LAST_LANE (gimple stmt
   gcc_unreachable ();
 }
 
+/* This should get expanded in the sanopt pass.  */
+
+static void
+expand_UBSAN_NULL (gimple stmt ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
 /* Routines to expand each internal function, indexed by function number.
    Each routine has the prototype:
 
--- gcc/gcc/passes.def.mp	2013-11-12 13:46:13.355182109 +0100
+++ gcc/gcc/passes.def	2013-11-12 13:46:49.825314074 +0100
@@ -55,6 +55,7 @@  along with GCC; see the file COPYING3.
       NEXT_PASS (pass_init_datastructures);
 
       NEXT_PASS (pass_build_ssa);
+      NEXT_PASS (pass_ubsan);
       NEXT_PASS (pass_early_warn_uninitialized);
       NEXT_PASS (pass_rebuild_cgraph_edges);
       NEXT_PASS (pass_inline_parameters);
@@ -309,6 +310,7 @@  along with GCC; see the file COPYING3.
   NEXT_PASS (pass_lower_complex_O0);
   NEXT_PASS (pass_asan_O0);
   NEXT_PASS (pass_tsan_O0);
+  NEXT_PASS (pass_sanopt);
   NEXT_PASS (pass_cleanup_eh);
   NEXT_PASS (pass_lower_resx);
   NEXT_PASS (pass_nrv);
--- gcc/gcc/cgraphunit.c.mp	2013-11-12 13:46:13.349182081 +0100
+++ gcc/gcc/cgraphunit.c	2013-11-12 13:46:49.819314047 +0100
@@ -825,7 +825,8 @@  varpool_finalize_decl (tree decl)
     varpool_analyze_node (node);
   /* Some frontends produce various interface variables after compilation
      finished.  */
-  if (cgraph_state == CGRAPH_STATE_FINISHED)
+  if (cgraph_state == CGRAPH_STATE_FINISHED
+      || (!flag_toplevel_reorder && cgraph_state == CGRAPH_STATE_EXPANSION))
     varpool_assemble_decl (node);
 }
 
--- gcc/gcc/internal-fn.def.mp	2013-11-12 13:46:13.353182099 +0100
+++ gcc/gcc/internal-fn.def	2013-11-12 16:11:39.394626260 +0100
@@ -44,3 +44,4 @@  DEF_INTERNAL_FN (GOMP_SIMD_LANE, ECF_NOV
 DEF_INTERNAL_FN (GOMP_SIMD_VF, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (GOMP_SIMD_LAST_LANE, ECF_CONST | ECF_LEAF | ECF_NOTHROW)
 DEF_INTERNAL_FN (ANNOTATE,  ECF_CONST | ECF_LEAF | ECF_NOTHROW)
+DEF_INTERNAL_FN (UBSAN_NULL, ECF_LEAF | ECF_NOTHROW)
--- gcc/gcc/testsuite/c-c++-common/ubsan/null-6.c.mp	2013-11-12 20:53:32.328616378 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/null-6.c	2013-11-12 20:58:57.540848964 +0100
@@ -0,0 +1,14 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+int
+main (void)
+{
+  unsigned long int *p = 0;
+  *p = 42;
+  return 0;
+}
+
+/* { dg-output "store to null pointer of type 'long unsigned int'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c.mp	2013-11-12 13:46:13.358182122 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/div-by-zero-3.c	2013-11-12 13:46:49.826314079 +0100
@@ -16,6 +16,6 @@  main (void)
   return 0;
 }
 
-/* { dg-output "division of -2147483648 by -1 cannot be represented in type int(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division of -2147483648 by -1 cannot be represented in type int(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*division of -2147483648 by -1 cannot be represented in type int(\n|\r\n|\r)" } */
+/* { dg-output "division of -2147483648 by -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division of -2147483648 by -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*division of -2147483648 by -1 cannot be represented in type 'int'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/null-10.c.mp	2013-11-12 21:09:50.462378790 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/null-10.c	2013-11-12 21:23:04.421294921 +0100
@@ -0,0 +1,14 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+int
+main (void)
+{
+  short *p = 0, *u;
+  *(u + *p) = 23;
+  return  0;
+}
+
+/* { dg-output "load of null pointer of type 'short int'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/null-4.c.mp	2013-11-12 20:19:42.514234257 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/null-4.c	2013-11-12 20:23:40.610106000 +0100
@@ -0,0 +1,15 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+int
+main (void)
+{
+  _Complex double *p = 0;
+  if (p[0])
+    return 42;
+  return 0;
+}
+
+/* { dg-output "load of null pointer of type 'complex double'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/null-8.c.mp	2013-11-12 21:07:21.276742012 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/null-8.c	2013-11-12 21:08:48.751120378 +0100
@@ -0,0 +1,17 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+struct S {
+  int i;
+};
+
+int
+main (void)
+{
+  struct S *s = 0;
+  return s->i;
+}
+
+/* { dg-output "member access within null pointer of type 'struct S'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/null-9.c.mp	2013-11-12 21:09:02.733171909 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/null-9.c	2013-11-12 21:09:29.331286963 +0100
@@ -0,0 +1,17 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+union U {
+  int i;
+};
+
+int
+main (void)
+{
+  union U *u = 0;
+  return u->i;
+}
+
+/* { dg-output "member access within null pointer of type 'union U'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/null-11.c.mp	2013-11-12 21:40:24.277303514 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/null-11.c	2013-11-12 21:41:38.642575471 +0100
@@ -0,0 +1,17 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+struct S {
+  int i;
+};
+
+int
+main (void)
+{
+  struct S **s = 0;
+  return (*s)->i;
+}
+
+/* { dg-output "load of null pointer of type 'struct S \\*'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/null-7.c.mp	2013-11-12 20:59:18.131924558 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/null-7.c	2013-11-12 21:03:38.636874389 +0100
@@ -0,0 +1,18 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+int *
+gao (void)
+{
+  return 0;
+}
+
+int
+main (void)
+{
+  return *gao ();
+}
+
+/* { dg-output "load of null pointer of type 'int'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/null-5.c.mp	2013-11-12 20:25:46.755617396 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/null-5.c	2013-11-12 20:53:10.085525041 +0100
@@ -0,0 +1,17 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+typedef volatile const _Complex float *T;
+
+int
+main (void)
+{
+  T t = 0;
+  if (*t)
+    return 42;
+  return 0;
+}
+
+/* { dg-output "load of null pointer of type 'volatile const complex float'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/null-1.c.mp	2013-11-12 15:37:15.204806063 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/null-1.c	2013-11-12 19:02:55.500951377 +0100
@@ -0,0 +1,13 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+int
+main (void)
+{
+  int *p = 0;
+  return *p;
+}
+
+/* { dg-output "load of null pointer of type 'int'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/shift-1.c.mp	2013-11-12 13:46:13.359182126 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/shift-1.c	2013-11-12 13:46:49.827314083 +0100
@@ -23,9 +23,9 @@  main (void)
 
   return 0;
 }
-/* { dg-output "shift exponent 152 is too large for \[^\n\r]*-bit type int(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent 153 is too large for \[^\n\r]*-bit type int(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent 154 is too large for \[^\n\r]*-bit type int(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent 524 is too large for \[^\n\r]*-bit type long long unsigned int(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent 370 is too large for \[^\n\r]*-bit type int(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*shift exponent 402 is too large for \[^\n\r]*-bit type long int(\n|\r\n|\r)" } */
+/* { dg-output "shift exponent 152 is too large for \[^\n\r]*-bit type 'int'(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 153 is too large for \[^\n\r]*-bit type 'int'(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 154 is too large for \[^\n\r]*-bit type 'int'(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 524 is too large for \[^\n\r]*-bit type 'long long unsigned int'(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 370 is too large for \[^\n\r]*-bit type 'int'(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*shift exponent 402 is too large for \[^\n\r]*-bit type 'long int'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/null-3.c.mp	2013-11-12 19:49:30.552505623 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/null-3.c	2013-11-12 20:17:23.093674131 +0100
@@ -0,0 +1,19 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+int
+foo (int *p)
+{
+  return *p;
+}
+
+int
+main (void)
+{
+  int **p = 0;
+  return foo (*p);
+}
+
+/* { dg-output "load of null pointer of type 'int \\*'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/c-c++-common/ubsan/null-2.c.mp	2013-11-12 16:46:44.795566860 +0100
+++ gcc/gcc/testsuite/c-c++-common/ubsan/null-2.c	2013-11-12 19:08:06.356208894 +0100
@@ -0,0 +1,13 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=null -w" } */
+/* { dg-shouldfail "ubsan" } */
+/* { dg-skip-if "" { *-*-* } { "-flto" } { "" } } */
+
+int
+main (void)
+{
+  int ***ppp = 0;
+  return ***ppp;
+}
+
+/* { dg-output "load of null pointer of type 'int \\*\\*'(\n|\r\n|\r)" } */
--- gcc/gcc/testsuite/gcc.dg/ubsan/c99-shift-2.c.mp	2013-11-12 13:46:13.360182130 +0100
+++ gcc/gcc/testsuite/gcc.dg/ubsan/c99-shift-2.c	2013-11-12 13:46:49.827314083 +0100
@@ -7,4 +7,4 @@  main (void)
   int a = 1;
   a <<= 31;
 }
-/* { dg-output "left shift of 1 by 31 places cannot be represented in type int" } */
+/* { dg-output "left shift of 1 by 31 places cannot be represented in type 'int'" } */
--- gcc/gcc/asan.c.mp	2013-11-12 13:46:13.347182073 +0100
+++ gcc/gcc/asan.c	2013-11-12 20:09:49.424056584 +0100
@@ -40,6 +40,7 @@  along with GCC; see the file COPYING3.
 #include "alloc-pool.h"
 #include "cfgloop.h"
 #include "gimple-builder.h"
+#include "ubsan.h"
 
 /* AddressSanitizer finds out-of-bounds and use-after-free bugs
    with <2x slowdown on average.
@@ -2365,4 +2366,87 @@  make_pass_asan_O0 (gcc::context *ctxt)
   return new pass_asan_O0 (ctxt);
 }
 
+/* Perform optimization of sanitize functions.  */
+
+static unsigned int
+execute_sanopt (void)
+{
+  basic_block bb;
+
+  FOR_EACH_BB (bb)
+    {
+      gimple_stmt_iterator gsi;
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+	{
+	  gimple stmt = gsi_stmt (gsi);
+
+	  if (gimple_code (stmt) != GIMPLE_CALL)
+	    continue;
+
+	  if (gimple_call_internal_p (stmt))
+	    switch (gimple_call_internal_fn (stmt))
+	      {
+	      case IFN_UBSAN_NULL:
+		ubsan_expand_null_ifn (gsi);
+		break;
+	      default:
+		break;
+	      }
+
+	  if (dump_file && (dump_flags & TDF_DETAILS))
+	    {
+	      fprintf (dump_file, "Optimized\n  ");
+	      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+	      fprintf (dump_file, "\n");
+	    }
+	}
+    }
+  return 0;
+}
+
+static bool
+gate_sanopt (void)
+{
+  return flag_sanitize;
+}
+
+namespace {
+
+const pass_data pass_data_sanopt =
+{
+  GIMPLE_PASS, /* type */
+  "sanopt", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  true, /* has_gate */
+  true, /* has_execute */
+  TV_NONE, /* tv_id */
+  ( PROP_ssa | PROP_cfg | PROP_gimple_leh ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  ( TODO_verify_flow | TODO_verify_stmts
+    | TODO_update_ssa ), /* todo_flags_finish */
+};
+
+class pass_sanopt : public gimple_opt_pass
+{
+public:
+  pass_sanopt (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_sanopt, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate () { return gate_sanopt (); }
+  unsigned int execute () { return execute_sanopt (); }
+
+}; // class pass_sanopt
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_sanopt (gcc::context *ctxt)
+{
+  return new pass_sanopt (ctxt);
+}
+
 #include "gt-asan.h"
--- gcc/gcc/timevar.def.mp	2013-11-12 13:46:13.361182134 +0100
+++ gcc/gcc/timevar.def	2013-11-12 13:46:49.828314087 +0100
@@ -261,6 +261,7 @@  DEFTIMEVAR (TV_PLUGIN_INIT           , "
 DEFTIMEVAR (TV_PLUGIN_RUN            , "plugin execution")
 DEFTIMEVAR (TV_GIMPLE_SLSR           , "straight-line strength reduction")
 DEFTIMEVAR (TV_VTABLE_VERIFICATION   , "vtable verification")
+DEFTIMEVAR (TV_TREE_UBSAN            , "tree ubsan")
 
 /* Everything else in rest_of_compilation not included above.  */
 DEFTIMEVAR (TV_EARLY_LOCAL	     , "early local passes")