diff mbox

Ubsan load of bool/enum sanitization

Message ID 20131214195352.GF892@tucnak.redhat.com
State New
Headers show

Commit Message

Jakub Jelinek Dec. 14, 2013, 7:53 p.m. UTC
Hi!

This patch implements one of the few remaining missing ubsan
sanitizations, in particular instrumentation which complains about
loads of (non-bitfield) bool or enum (the latter in C++ only) value
that is outside of the range allowed (0/1 for bool, min/max for
minimum precision integer type holding all values as the standard says).

Is this still acceptable for trunk (it is missing part of a new feature
in 4.9, doesn't affect code unless one of -fsanitize={undefined,bool,enum}
is used)?

2013-12-14  Jakub Jelinek  <jakub@redhat.com>

	* ubsan.c: Include tree-ssanames.h, asan.h and gimplify-me.h.
	(ubsan_type_descriptor): Handle BOOLEAN_TYPE and ENUMERAL_TYPE
	like INTEGER_TYPE.
	(instrument_bool_enum_load): New function.
	(ubsan_pass): Call it.
	(gate_ubsan): Also enable for SANITIZE_BOOL or SANITIZE_ENUM.
	* asan.c (create_cond_insert_point): No longer static.
	* asan.h (create_cond_insert_point): Declare.
	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE): New
	built-in.
	* opts.c (common_handle_option): Handle -fsanitize=bool and
	-fsanitize=enum.
	* builtins.c (fold_builtin_memory_op): When sanitizing bool
	and enum loads, don't use enum or bool types for memcpy folding.
	* flag-types.h (SANITIZE_BOOL, SANITIZE_ENUM): New.
	(SANITIZE_UNDEFINED): Or these in.

	* c-c++-common/ubsan/load-bool-enum.c: New test.


	Jakub

Comments

Jeff Law Dec. 16, 2013, 6:25 p.m. UTC | #1
On 12/14/13 12:53, Jakub Jelinek wrote:
> Hi!
>
> This patch implements one of the few remaining missing ubsan
> sanitizations, in particular instrumentation which complains about
> loads of (non-bitfield) bool or enum (the latter in C++ only) value
> that is outside of the range allowed (0/1 for bool, min/max for
> minimum precision integer type holding all values as the standard says).
>
> Is this still acceptable for trunk (it is missing part of a new feature
> in 4.9, doesn't affect code unless one of -fsanitize={undefined,bool,enum}
> is used)?
>
> 2013-12-14  Jakub Jelinek  <jakub@redhat.com>
>
> 	* ubsan.c: Include tree-ssanames.h, asan.h and gimplify-me.h.
> 	(ubsan_type_descriptor): Handle BOOLEAN_TYPE and ENUMERAL_TYPE
> 	like INTEGER_TYPE.
> 	(instrument_bool_enum_load): New function.
> 	(ubsan_pass): Call it.
> 	(gate_ubsan): Also enable for SANITIZE_BOOL or SANITIZE_ENUM.
> 	* asan.c (create_cond_insert_point): No longer static.
> 	* asan.h (create_cond_insert_point): Declare.
> 	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE): New
> 	built-in.
> 	* opts.c (common_handle_option): Handle -fsanitize=bool and
> 	-fsanitize=enum.
> 	* builtins.c (fold_builtin_memory_op): When sanitizing bool
> 	and enum loads, don't use enum or bool types for memcpy folding.
> 	* flag-types.h (SANITIZE_BOOL, SANITIZE_ENUM): New.
> 	(SANITIZE_UNDEFINED): Or these in.
>
> 	* c-c++-common/ubsan/load-bool-enum.c: New test.
As you note, there's some question as to whether or not this should be 
acceptable for 4.9.  Yes it's well contained, but we really need to draw 
the line.  Is this the last thing for the sanitizers that is still under 
consideration?

Also, doesn't C allow objects with an enum type to contain values not in 
the enumerated value list?  Do TYPE_MIN/TYPE_MAX give the range of the 
enums or the range of the underlying mode for the object?

jeff
Jakub Jelinek Dec. 16, 2013, 6:40 p.m. UTC | #2
On Mon, Dec 16, 2013 at 11:25:33AM -0700, Jeff Law wrote:
> As you note, there's some question as to whether or not this should
> be acceptable for 4.9.  Yes it's well contained, but we really need
> to draw the line.  Is this the last thing for the sanitizers that is
> still under consideration?

It can be the last thing, sure.  I think the still unimplemented and
potentially useful are the floating point overflow sanitization (haven't
looked yet what exactly it is, I suppose casts from floating point to
integers where the values are out of range, but dunno exactly) and
they have also some __builtin_object_size based bounds checking.

> Also, doesn't C allow objects with an enum type to contain values
> not in the enumerated value list?  Do TYPE_MIN/TYPE_MAX give the

C allows this, but in C the TYPE_MIN/TYPE_MAX on the ENUMERAL_TYPE is
the full mode range and TREE_TYPE on the ENUMERAL_TYPE is NULL, only
C++ sets it to some limited precision type.  The testcase the patch
adds even verifies we don't complain in C for it.

> range of the enums or the range of the underlying mode for the
> object?

	Jakub
Jakub Jelinek Dec. 16, 2013, 6:43 p.m. UTC | #3
On Mon, Dec 16, 2013 at 07:40:16PM +0100, Jakub Jelinek wrote:
> It can be the last thing, sure.  I think the still unimplemented and
> potentially useful are the floating point overflow sanitization (haven't
> looked yet what exactly it is, I suppose casts from floating point to
> integers where the values are out of range, but dunno exactly) and
> they have also some __builtin_object_size based bounds checking.

Oh, and then there is sanitization of nonnull arguments and returns_nonnull
return values which ideally we should add, clang doesn't have it (yet?), but
it is really desirable when we aggressively optimize based on those
attributes.  We need to discuss with compiler-rt ubsan upstream first
though, unless we want to add the entrypoint as a GCC only libubsan addition.

	Jakub
Jeff Law Dec. 16, 2013, 8:55 p.m. UTC | #4
On 12/16/13 11:43, Jakub Jelinek wrote:
> On Mon, Dec 16, 2013 at 07:40:16PM +0100, Jakub Jelinek wrote:
>> It can be the last thing, sure.  I think the still unimplemented and
>> potentially useful are the floating point overflow sanitization (haven't
>> looked yet what exactly it is, I suppose casts from floating point to
>> integers where the values are out of range, but dunno exactly) and
>> they have also some __builtin_object_size based bounds checking.
>
> Oh, and then there is sanitization of nonnull arguments and returns_nonnull
> return values which ideally we should add, clang doesn't have it (yet?), but
> it is really desirable when we aggressively optimize based on those
> attributes.  We need to discuss with compiler-rt ubsan upstream first
> though, unless we want to add the entrypoint as a GCC only libubsan addition.
This, IMHO, clearly needs to wait.

jeff
Joseph Myers Dec. 18, 2013, 5:48 p.m. UTC | #5
On Mon, 16 Dec 2013, Jakub Jelinek wrote:

> On Mon, Dec 16, 2013 at 07:40:16PM +0100, Jakub Jelinek wrote:
> > It can be the last thing, sure.  I think the still unimplemented and
> > potentially useful are the floating point overflow sanitization (haven't
> > looked yet what exactly it is, I suppose casts from floating point to
> > integers where the values are out of range, but dunno exactly) and
> > they have also some __builtin_object_size based bounds checking.
> 
> Oh, and then there is sanitization of nonnull arguments and returns_nonnull
> return values which ideally we should add, clang doesn't have it (yet?), but
> it is really desirable when we aggressively optimize based on those
> attributes.  We need to discuss with compiler-rt ubsan upstream first
> though, unless we want to add the entrypoint as a GCC only libubsan addition.

I think there's probably more for future versions once you get into adding 
new entry points.  See e.g. 
<http://gcc.gnu.org/ml/gcc-patches/2013-09/msg01162.html> about not being 
able to check systematically that the size in bytes of a VLA, including a 
constant-size array of VLAs, does not exceed target PTRDIFF_MAX, for lack 
of libubsan support.

More generally: look at the list of undefined behavior in C11 J.2.  For 
each item there, it should be possible to say one (or more) of:

* GCC bounds the effects of this undefined behavior.

* This undefined behavior is detected at runtime with (specified 
sanitization option).

* This undefined behavior is critical undefined behavior as defined in 
L.3.

Perhaps a table to that effect, along with one for GCC extensions for 
which sanitization could usefully apply, should go on the wiki.  We could 
then see what sanitization (or other bounding) features are needed in 
order to provide an option / options that would conform to Annex L and 
define __STDC_ANALYZABLE__.
Joseph Myers Dec. 18, 2013, 5:56 p.m. UTC | #6
On Mon, 16 Dec 2013, Jakub Jelinek wrote:

> It can be the last thing, sure.  I think the still unimplemented and
> potentially useful are the floating point overflow sanitization (haven't
> looked yet what exactly it is, I suppose casts from floating point to
> integers where the values are out of range, but dunno exactly) and

Note that under Annex F that's only unspecified value plus "invalid" 
exception, rather than undefined behavior (though that issue is covered by 
allowing this checking to be enabled / disabled independent of the other 
cases).  (Reliably getting the "invalid" exception is one of the many 
Annex F pieces not implemented in GCC.)

I think it would be most appropriate for floating-point conversion to 
bit-fields in C to count as out of range (with sanitization / exception as 
appropriate) based on the range of the bit-field, but in C++ it should 
probably be based on the range of the underlying type not taking into 
account the bit-field width, with conversion from that type to the 
bit-field then being modulo, in accordance with the principle that 
bit-field width is not part of the type in C++.
Jakub Jelinek Dec. 19, 2013, 4:42 p.m. UTC | #7
On Mon, Dec 16, 2013 at 01:55:50PM -0700, Jeff Law wrote:
> On 12/16/13 11:43, Jakub Jelinek wrote:
> >On Mon, Dec 16, 2013 at 07:40:16PM +0100, Jakub Jelinek wrote:
> >>It can be the last thing, sure.  I think the still unimplemented and
> >>potentially useful are the floating point overflow sanitization (haven't
> >>looked yet what exactly it is, I suppose casts from floating point to
> >>integers where the values are out of range, but dunno exactly) and
> >>they have also some __builtin_object_size based bounds checking.
> >
> >Oh, and then there is sanitization of nonnull arguments and returns_nonnull
> >return values which ideally we should add, clang doesn't have it (yet?), but
> >it is really desirable when we aggressively optimize based on those
> >attributes.  We need to discuss with compiler-rt ubsan upstream first
> >though, unless we want to add the entrypoint as a GCC only libubsan addition.
> This, IMHO, clearly needs to wait.

You mean -fsanitize={bool,enum} (the already posted patch, which just
discovered a bug in GCC itself today), -fsanitize=nonnull (the non-written future
task), something else?

	Jakub
Jeff Law Dec. 19, 2013, 4:51 p.m. UTC | #8
On 12/19/13 09:42, Jakub Jelinek wrote:
> On Mon, Dec 16, 2013 at 01:55:50PM -0700, Jeff Law wrote:
>> On 12/16/13 11:43, Jakub Jelinek wrote:
>>> On Mon, Dec 16, 2013 at 07:40:16PM +0100, Jakub Jelinek wrote:
>>>> It can be the last thing, sure.  I think the still unimplemented and
>>>> potentially useful are the floating point overflow sanitization (haven't
>>>> looked yet what exactly it is, I suppose casts from floating point to
>>>> integers where the values are out of range, but dunno exactly) and
>>>> they have also some __builtin_object_size based bounds checking.
>>>
>>> Oh, and then there is sanitization of nonnull arguments and returns_nonnull
>>> return values which ideally we should add, clang doesn't have it (yet?), but
>>> it is really desirable when we aggressively optimize based on those
>>> attributes.  We need to discuss with compiler-rt ubsan upstream first
>>> though, unless we want to add the entrypoint as a GCC only libubsan addition.
>> This, IMHO, clearly needs to wait.
>
> You mean -fsanitize={bool,enum} (the already posted patch, which just
> discovered a bug in GCC itself today), -fsanitize=nonnull (the non-written future
> task), something else?
I was referring to sanitize-null.

jeff
Jeff Law Dec. 20, 2013, 5:14 a.m. UTC | #9
On 12/16/13 11:40, Jakub Jelinek wrote:
> On Mon, Dec 16, 2013 at 11:25:33AM -0700, Jeff Law wrote:
>> As you note, there's some question as to whether or not this should
>> be acceptable for 4.9.  Yes it's well contained, but we really need
>> to draw the line.  Is this the last thing for the sanitizers that is
>> still under consideration?
>
> It can be the last thing, sure.  I think the still unimplemented and
> potentially useful are the floating point overflow sanitization (haven't
> looked yet what exactly it is, I suppose casts from floating point to
> integers where the values are out of range, but dunno exactly) and
> they have also some __builtin_object_size based bounds checking.
As we both know, there's always more that we can do :-)  Given the 
self-contained nature of this code, my concern is more about moving 
focus from feature work to fixing bugs.  The regression list is, umm, 
large and not shrinking at a rate that gives me much confidence that 
we're on track for a spring release.

I'll take a closer look at the actual patch momentarily.

>
>> Also, doesn't C allow objects with an enum type to contain values
>> not in the enumerated value list?  Do TYPE_MIN/TYPE_MAX give the
>
> C allows this, but in C the TYPE_MIN/TYPE_MAX on the ENUMERAL_TYPE is
> the full mode range and TREE_TYPE on the ENUMERAL_TYPE is NULL, only
> C++ sets it to some limited precision type.  The testcase the patch
> adds even verifies we don't complain in C for it.
Excellent.  Thanks for the clarification.

Jeff
Jeff Law Dec. 20, 2013, 5:22 a.m. UTC | #10
On 12/14/13 12:53, Jakub Jelinek wrote:
> 2013-12-14  Jakub Jelinek  <jakub@redhat.com>
>
> 	* ubsan.c: Include tree-ssanames.h, asan.h and gimplify-me.h.
> 	(ubsan_type_descriptor): Handle BOOLEAN_TYPE and ENUMERAL_TYPE
> 	like INTEGER_TYPE.
> 	(instrument_bool_enum_load): New function.
> 	(ubsan_pass): Call it.
> 	(gate_ubsan): Also enable for SANITIZE_BOOL or SANITIZE_ENUM.
> 	* asan.c (create_cond_insert_point): No longer static.
> 	* asan.h (create_cond_insert_point): Declare.
> 	* sanitizer.def (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE): New
> 	built-in.
> 	* opts.c (common_handle_option): Handle -fsanitize=bool and
> 	-fsanitize=enum.
> 	* builtins.c (fold_builtin_memory_op): When sanitizing bool
> 	and enum loads, don't use enum or bool types for memcpy folding.
> 	* flag-types.h (SANITIZE_BOOL, SANITIZE_ENUM): New.
> 	(SANITIZE_UNDEFINED): Or these in.
>
> 	* c-c++-common/ubsan/load-bool-enum.c: New test.
OK with nits noted below fixed:

> +
> +  int modebitsize = GET_MODE_BITSIZE (TYPE_MODE (type));
> +  HOST_WIDE_INT bitsize, bitpos;
> +  tree offset;
> +  enum machine_mode mode;
> +  int volatilep = 0, unsignedp = 0;
> +  tree base = get_inner_reference (rhs, &bitsize, &bitpos, &offset, &mode,
> +				   &unsignedp, &volatilep, false);
> +  tree utype = build_nonstandard_integer_type (modebitsize, 1);
> +
> +  if ((TREE_CODE (base) == VAR_DECL && DECL_HARD_REGISTER (base))
> +      || (bitpos % modebitsize) != 0
> +      || bitsize != modebitsize
> +      || GET_MODE_BITSIZE (TYPE_MODE (utype)) != modebitsize
> +      || TREE_CODE (gimple_assign_lhs (stmt)) != SSA_NAME)
> +    return;
> +
> +  location_t loc = gimple_location (stmt);
> +  tree ptype = build_pointer_type (TREE_TYPE (rhs));
> +  tree atype = reference_alias_ptr_type (rhs);
> +  gimple g = gimple_build_assign (make_ssa_name (ptype, NULL),
> +				  build_fold_addr_expr (rhs));
> +  gimple_set_location (g, loc);
> +  gsi_insert_before (gsi, g, GSI_SAME_STMT);
> +  tree mem = build2 (MEM_REF, utype, gimple_assign_lhs (g),
> +		     build_int_cst (atype, 0));
> +  tree urhs = make_ssa_name (utype, NULL);
> +  g = gimple_build_assign (urhs, mem);
> +  gimple_set_location (g, loc);
> +  gsi_insert_before (gsi, g, GSI_SAME_STMT);
> +  minv = fold_convert (utype, minv);
> +  maxv = fold_convert (utype, maxv);
> +  if (!integer_zerop (minv))
> +    {
> +      g = gimple_build_assign_with_ops (MINUS_EXPR,
> +					make_ssa_name (utype, NULL),
> +					urhs, minv);
> +      gimple_set_location (g, loc);
> +      gsi_insert_before (gsi, g, GSI_SAME_STMT);
> +    }
> +
> +  gimple_stmt_iterator gsi2 = *gsi;
> +  basic_block then_bb, fallthru_bb;
> +  *gsi = create_cond_insert_point (gsi, /*before_p=*/true,
> +				   /*then_more_likely_p=*/false,
> +				   /*create_then_fallthru_edge=*/true,
> +				   &then_bb, &fallthru_bb);
Ick (comments embedded in argumust list).  Is there some compelling 
reason for those comments?

OK with that trivial fix.

jeff
Jakub Jelinek Dec. 20, 2013, 8:28 a.m. UTC | #11
On Thu, Dec 19, 2013 at 10:22:38PM -0700, Jeff Law wrote:
> >+  *gsi = create_cond_insert_point (gsi, /*before_p=*/true,
> >+				   /*then_more_likely_p=*/false,
> >+				   /*create_then_fallthru_edge=*/true,
> >+				   &then_bb, &fallthru_bb);
> Ick (comments embedded in argumust list).  Is there some compelling
> reason for those comments?

That is a style used heavily e.g. in the C++ frontend, just an attempt
to make the code slightly more readable what exactly you are passing.
But it isn't that important, so I'll remove it.

> OK with that trivial fix.

Thanks.

	Jakub
diff mbox

Patch

--- gcc/ubsan.c.jj	2013-12-10 08:52:13.000000000 +0100
+++ gcc/ubsan.c	2013-12-14 19:39:29.099146079 +0100
@@ -43,6 +43,9 @@  along with GCC; see the file COPYING3.
 #include "c-family/c-common.h"
 #include "rtl.h"
 #include "expr.h"
+#include "tree-ssanames.h"
+#include "asan.h"
+#include "gimplify-me.h"
 
 /* Map from a tree to a VAR_DECL tree.  */
 
@@ -344,6 +347,8 @@  ubsan_type_descriptor (tree type, bool w
 
   switch (TREE_CODE (type))
     {
+    case BOOLEAN_TYPE:
+    case ENUMERAL_TYPE:
     case INTEGER_TYPE:
       tkind = 0x0000;
       break;
@@ -733,6 +738,106 @@  instrument_si_overflow (gimple_stmt_iter
     }
 }
 
+/* Instrument loads from (non-bitfield) bool and C++ enum values
+   to check if the memory value is outside of the range of the valid
+   type values.  */
+
+static void
+instrument_bool_enum_load (gimple_stmt_iterator *gsi)
+{
+  gimple stmt = gsi_stmt (*gsi);
+  tree rhs = gimple_assign_rhs1 (stmt);
+  tree type = TREE_TYPE (rhs);
+  tree minv = NULL_TREE, maxv = NULL_TREE;
+
+  if (TREE_CODE (type) == BOOLEAN_TYPE && (flag_sanitize & SANITIZE_BOOL))
+    {
+      minv = boolean_false_node;
+      maxv = boolean_true_node;
+    }
+  else if (TREE_CODE (type) == ENUMERAL_TYPE
+	   && (flag_sanitize & SANITIZE_ENUM)
+	   && TREE_TYPE (type) != NULL_TREE
+	   && TREE_CODE (TREE_TYPE (type)) == INTEGER_TYPE
+	   && (TYPE_PRECISION (TREE_TYPE (type))
+	       < GET_MODE_PRECISION (TYPE_MODE (type))))
+    {
+      minv = TYPE_MIN_VALUE (TREE_TYPE (type));
+      maxv = TYPE_MAX_VALUE (TREE_TYPE (type));
+    }
+  else
+    return;
+
+  int modebitsize = GET_MODE_BITSIZE (TYPE_MODE (type));
+  HOST_WIDE_INT bitsize, bitpos;
+  tree offset;
+  enum machine_mode mode;
+  int volatilep = 0, unsignedp = 0;
+  tree base = get_inner_reference (rhs, &bitsize, &bitpos, &offset, &mode,
+				   &unsignedp, &volatilep, false);
+  tree utype = build_nonstandard_integer_type (modebitsize, 1);
+
+  if ((TREE_CODE (base) == VAR_DECL && DECL_HARD_REGISTER (base))
+      || (bitpos % modebitsize) != 0
+      || bitsize != modebitsize
+      || GET_MODE_BITSIZE (TYPE_MODE (utype)) != modebitsize
+      || TREE_CODE (gimple_assign_lhs (stmt)) != SSA_NAME)
+    return;
+
+  location_t loc = gimple_location (stmt);
+  tree ptype = build_pointer_type (TREE_TYPE (rhs));
+  tree atype = reference_alias_ptr_type (rhs);
+  gimple g = gimple_build_assign (make_ssa_name (ptype, NULL),
+				  build_fold_addr_expr (rhs));
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+  tree mem = build2 (MEM_REF, utype, gimple_assign_lhs (g),
+		     build_int_cst (atype, 0));
+  tree urhs = make_ssa_name (utype, NULL);
+  g = gimple_build_assign (urhs, mem);
+  gimple_set_location (g, loc);
+  gsi_insert_before (gsi, g, GSI_SAME_STMT);
+  minv = fold_convert (utype, minv);
+  maxv = fold_convert (utype, maxv);
+  if (!integer_zerop (minv))
+    {
+      g = gimple_build_assign_with_ops (MINUS_EXPR,
+					make_ssa_name (utype, NULL),
+					urhs, minv);
+      gimple_set_location (g, loc);
+      gsi_insert_before (gsi, g, GSI_SAME_STMT);
+    }
+
+  gimple_stmt_iterator gsi2 = *gsi;
+  basic_block then_bb, fallthru_bb;
+  *gsi = create_cond_insert_point (gsi, /*before_p=*/true,
+				   /*then_more_likely_p=*/false,
+				   /*create_then_fallthru_edge=*/true,
+				   &then_bb, &fallthru_bb);
+  g = gimple_build_cond (GT_EXPR, gimple_assign_lhs (g),
+			 int_const_binop (MINUS_EXPR, maxv, minv),
+			 NULL_TREE, NULL_TREE);
+  gimple_set_location (g, loc);
+  gsi_insert_after (gsi, g, GSI_NEW_STMT);
+
+  gimple_assign_set_rhs_with_ops (&gsi2, NOP_EXPR, urhs, NULL_TREE);
+  update_stmt (stmt);
+
+  tree data = ubsan_create_data ("__ubsan_invalid_value_data",
+				 loc, NULL,
+				 ubsan_type_descriptor (type, false),
+				 NULL_TREE);
+  data = build_fold_addr_expr_loc (loc, data);
+  tree fn = builtin_decl_explicit (BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE);
+
+  gsi2 = gsi_after_labels (then_bb);
+  tree val = force_gimple_operand_gsi (&gsi2, ubsan_encode_value (urhs),
+				       true, NULL_TREE, true, GSI_SAME_STMT);
+  g = gimple_build_call (fn, 2, data, val);
+  gimple_set_location (g, loc);
+  gsi_insert_before (&gsi2, g, GSI_SAME_STMT);
+}
+
 /* Gate and execute functions for ubsan pass.  */
 
 static unsigned int
@@ -764,6 +869,10 @@  ubsan_pass (void)
 		instrument_null (gsi, false);
 	    }
 
+	  if (flag_sanitize & (SANITIZE_BOOL | SANITIZE_ENUM)
+	      && gimple_assign_load_p (stmt))
+	    instrument_bool_enum_load (&gsi);
+
 	  gsi_next (&gsi);
 	}
     }
@@ -773,7 +882,8 @@  ubsan_pass (void)
 static bool
 gate_ubsan (void)
 {
-  return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW);
+  return flag_sanitize & (SANITIZE_NULL | SANITIZE_SI_OVERFLOW
+			  | SANITIZE_BOOL | SANITIZE_ENUM);
 }
 
 namespace {
--- gcc/asan.c.jj	2013-12-10 08:52:09.000000000 +0100
+++ gcc/asan.c	2013-12-14 15:20:11.908858259 +0100
@@ -1337,7 +1337,7 @@  report_error_func (bool is_store, int si
     same as what ITER was pointing to prior to calling this function,
     if BEFORE_P is true; otherwise, it is its following statement.  */
 
-static gimple_stmt_iterator
+gimple_stmt_iterator
 create_cond_insert_point (gimple_stmt_iterator *iter,
 			  bool before_p,
 			  bool then_more_likely_p,
--- gcc/sanitizer.def.jj	2013-12-05 09:39:36.000000000 +0100
+++ gcc/sanitizer.def	2013-12-14 17:14:23.621861421 +0100
@@ -331,3 +331,7 @@  DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HAN
 		      "__ubsan_handle_negate_overflow",
 		      BT_FN_VOID_PTR_PTR,
 		      ATTR_COLD_NOTHROW_LEAF_LIST)
+DEF_SANITIZER_BUILTIN(BUILT_IN_UBSAN_HANDLE_LOAD_INVALID_VALUE,
+		      "__ubsan_handle_load_invalid_value",
+		      BT_FN_VOID_PTR_PTR,
+		      ATTR_COLD_NOTHROW_LEAF_LIST)
--- gcc/asan.h.jj	2013-11-28 08:29:48.000000000 +0100
+++ gcc/asan.h	2013-12-14 15:22:31.738103074 +0100
@@ -29,6 +29,9 @@  extern bool asan_protect_global (tree);
 extern void initialize_sanitizer_builtins (void);
 extern tree asan_dynamic_init_call (bool);
 
+extern gimple_stmt_iterator create_cond_insert_point
+     (gimple_stmt_iterator *, bool, bool, bool, basic_block *, basic_block *);
+
 /* Alias set for accessing the shadow memory.  */
 extern alias_set_type asan_shadow_set;
 
--- gcc/opts.c.jj	2013-12-05 09:39:37.000000000 +0100
+++ gcc/opts.c	2013-12-14 19:38:43.048383201 +0100
@@ -1462,6 +1462,8 @@  common_handle_option (struct gcc_options
 	      { "null", SANITIZE_NULL, sizeof "null" - 1 },
 	      { "signed-integer-overflow", SANITIZE_SI_OVERFLOW,
 		sizeof "signed-integer-overflow" -1 },
+	      { "bool", SANITIZE_BOOL, sizeof "bool" - 1 },
+	      { "enum", SANITIZE_ENUM, sizeof "enum" - 1 },
 	      { NULL, 0, 0 }
 	    };
 	    const char *comma;
--- gcc/builtins.c.jj	2013-12-02 14:33:32.000000000 +0100
+++ gcc/builtins.c	2013-12-14 17:10:00.442238386 +0100
@@ -8912,6 +8912,29 @@  fold_builtin_memory_op (location_t loc,
       off0 = build_int_cst (build_pointer_type_for_mode (char_type_node,
 							 ptr_mode, true), 0);
 
+      /* For -fsanitize={bool,enum} make sure the load isn't performed in
+	 the bool or enum type.  */
+      if (((flag_sanitize & SANITIZE_BOOL)
+	   && TREE_CODE (desttype) == BOOLEAN_TYPE)
+	  || ((flag_sanitize & SANITIZE_ENUM)
+	      && TREE_CODE (desttype) == ENUMERAL_TYPE))
+	{
+	  tree destitype
+	    = lang_hooks.types.type_for_mode (TYPE_MODE (desttype),
+					      TYPE_UNSIGNED (desttype));
+	  desttype = build_aligned_type (destitype, TYPE_ALIGN (desttype));
+	}
+      if (((flag_sanitize & SANITIZE_BOOL)
+	   && TREE_CODE (srctype) == BOOLEAN_TYPE)
+	  || ((flag_sanitize & SANITIZE_ENUM)
+	      && TREE_CODE (srctype) == ENUMERAL_TYPE))
+	{
+	  tree srcitype
+	    = lang_hooks.types.type_for_mode (TYPE_MODE (srctype),
+					      TYPE_UNSIGNED (srctype));
+	  srctype = build_aligned_type (srcitype, TYPE_ALIGN (srctype));
+	}
+
       destvar = dest;
       STRIP_NOPS (destvar);
       if (TREE_CODE (destvar) == ADDR_EXPR
--- gcc/flag-types.h.jj	2013-12-05 09:39:38.000000000 +0100
+++ gcc/flag-types.h	2013-12-14 14:00:29.434032718 +0100
@@ -216,9 +216,11 @@  enum sanitize_code {
   SANITIZE_NULL = 1 << 7,
   SANITIZE_RETURN = 1 << 8,
   SANITIZE_SI_OVERFLOW = 1 << 9,
+  SANITIZE_BOOL = 1 << 10,
+  SANITIZE_ENUM = 1 << 11,
   SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
 		       | SANITIZE_VLA | SANITIZE_NULL | SANITIZE_RETURN
-		       | SANITIZE_SI_OVERFLOW
+		       | SANITIZE_SI_OVERFLOW | SANITIZE_BOOL | SANITIZE_ENUM
 };
 
 /* flag_vtable_verify initialization levels. */
--- gcc/testsuite/c-c++-common/ubsan/load-bool-enum.c.jj	2013-12-14 19:25:57.271405245 +0100
+++ gcc/testsuite/c-c++-common/ubsan/load-bool-enum.c	2013-12-14 19:41:13.409599574 +0100
@@ -0,0 +1,29 @@ 
+/* { dg-do run } */
+/* { dg-options "-fsanitize=bool,enum" } */
+
+#ifndef __cplusplus
+#define bool _Bool
+#endif
+enum A { B = -3, C = 2 } a;
+bool b;
+
+__attribute__((noinline, noclone)) enum A
+foo (bool *p)
+{
+  *p = b;   /* { dg-output "load-bool-enum.c:13:\[^\n\r]*runtime error: load of value 4, which is not a valid value for type '(_B|b)ool'(\n|\r\n|\r)" } */
+  return a; /* { dg-output "\[^\n\r]*load-bool-enum.c:14:\[^\n\r]*runtime error: load of value 9, which is not a valid value for type 'A'(\n|\r\n|\r)" { target c++ } } */
+}
+
+int
+main ()
+{
+  char c = 4;
+  int d = 9;
+  if (sizeof (int) != sizeof (a) || sizeof (b) != 1)
+    return 0;
+  __builtin_memcpy (&a, &d, sizeof (int));
+  __builtin_memcpy (&b, &c, 1);
+  bool e;
+  foo (&e);
+  return 0;
+}