diff mbox

[PATCHv2] Don't expand string/memory builtins if ASan is enabled.

Message ID 54465EF3.5060601@partner.samsung.com
State New
Headers show

Commit Message

max Oct. 21, 2014, 1:26 p.m. UTC
Hi,

this is the second version of the patch. Here the major changes from the 
previous one:

1) Added a new intercepted_p parameter in get_mem_refs_of_builtin_call 
to decide whether builtin function should/shouldn't be instrumented.

2) Changed instrument_mem_region_access function. Now, we update 
asan_mem_ref_ht with (base, size_in_bytes), if we can determine access 
size during compile time.

3) Removed ASAN_CHECK_START_INSTRUMENTED and ASAN_CHECK_END_INSTRUMENTED 
from asan_check_flags since we don't instrument base and end of
memory region with access size 1 anymore.

4) Specified builtins that shouldn't be expanded explicitly in 
gcc/builtins.c.

Regtested / bootrapped on x86_64-unknown-linux-gnu.

-Maxim
On 10/17/2014 05:03 PM, Jakub Jelinek wrote:
> On Fri, Oct 17, 2014 at 05:01:33PM +0400, Yury Gribov wrote:
>> On 10/17/2014 04:24 PM, Jakub Jelinek wrote:
>>>> +/* Returns TRUE if given FCODE corresponds to string or memory builtin function.
>>>> + */
>>>> +
>>>> +static inline bool
>>>> +is_memory_builtin (enum built_in_function fcode)
>>>> +{
>>>> +  return fcode <= BUILT_IN_STRSTR && fcode >= BUILT_IN_BCMP;
>>> This is too fragile and ugly.
>>> IMHO you should list (supposedly not in a special inline, but directly
>>> where you use it) in a switch all the builtins you don't want to expand.
>> We already do this for BUILT_IN_ASAN_REPORT_LOAD1 ... BUILT_IN_ASAN_STOREN
> I know, but it is still a coherent sent of builtins for very similar
> purposes, many of them sorted by increasing size number.
>
>> but I agree that this one is more ugly.
> The memops builtins are just random bag of them, it is expected many people
> will add builtins into that range and outside of that range.
>
> 	Jakub
>

Comments

Yury Gribov Oct. 23, 2014, 8:26 a.m. UTC | #1
On 10/21/2014 05:26 PM, Maxim Ostapenko wrote:
> Hi,
>
> this is the second version of the patch. Here the major changes from the
> previous one:

[snip]

>       case BUILT_IN_BCMP:
> +      *intercepted_p = false;

The code which identifies interceptors seems to be duplicated in 
expand_builtin.  What about factoring this out to some 
is_asan_intercepted() in asan.h?

-Y
Jakub Jelinek Oct. 27, 2014, 2:34 p.m. UTC | #2
On Thu, Oct 23, 2014 at 12:26:48PM +0400, Yury Gribov wrote:
> On 10/21/2014 05:26 PM, Maxim Ostapenko wrote:
> >Hi,
> >
> >this is the second version of the patch. Here the major changes from the
> >previous one:
> 
> [snip]
> 
> >      case BUILT_IN_BCMP:
> >+      *intercepted_p = false;
> 
> The code which identifies interceptors seems to be duplicated in
> expand_builtin.  What about factoring this out to some is_asan_intercepted()
> in asan.h?

I agree.

BTW, when you handle a builtin, but set *intercepted_p to false, is the
point just that you don't instrument e.g. scalar accesses to that memory
region afterwards?  I mean:
memcpy (p, "abc", 3);
p[0] = 'd';
you don't really have to instrument p[0] store, because supposedly memcpy
call has already verified p[0] through p[2] is writable.

	Jakub
diff mbox

Patch

gcc/ChangeLog:

2014-10-21  Max Ostapenko  <m.ostapenko@partner.samsung.com>

	* asan.c (asan_mem_ref_hasher::hash): Remove MEM_REF access size from
	hash value construction. Call iterative_hash_expr instead of explicit
	hash building.
	(asan_mem_ref_hasher::equal): Change condition.
	(has_mem_ref_been_instrumented): Likewise.
	(update_mem_ref_hash_table): Likewise.
	(maybe_update_mem_ref_hash_table): New function.
	(instrument_strlen_call): Removed.
	(get_mem_refs_of_builtin_call): Handle new parameter.
	(instrument_builtin_call): Call maybe_update_mem_ref_hash_table instead
	of instrument_mem_region_access if intercepted_p is true.
	(instrument_mem_region_access): Instrument only base with len instead of
	base and end with 1.
	(build_check_stmt): Remove start_instrumented and end_instrumented
	parameters.
	(enum asan_check_flags): Remove ASAN_CHECK_START_INSTRUMENTED and
	ASAN_CHECK_END_INSTRUMENTED. Change ASAN_CHECK_LAST.
	(asan_expand_check_ifn): Remove start_instrumented and end_instrumented.
	* builtins.c (expand_builtin): Don't expand string/memory builtin functions
	that have interceptors in libsanitizer if ASan is enabled.

gcc/testsuite/ChangeLog:

2014-10-21  Max Ostapenko  <m.ostapenko@partner.samsung.com>

	* c-c++-common/asan/no-redundant-instrumentation-1.c: Updated test.
	* c-c++-common/asan/no-redundant-instrumentation-4.c: Likewise.
	* c-c++-common/asan/no-redundant-instrumentation-5.c: Likewise.
	* c-c++-common/asan/no-redundant-instrumentation-6.c: Likewise.
	* c-c++-common/asan/no-redundant-instrumentation-7.c: Likewise.
	* c-c++-common/asan/no-redundant-instrumentation-8.c: Likewise.
	* c-c++-common/asan/no-redundant-instrumentation-2.c: Removed.
	* c-c++-common/asan/no-redundant-instrumentation-9.c: Likewise.
	* c-c++-common/asan/no-redundant-instrumentation-10.c: New test.
	* c-c++-common/asan/no-redundant-instrumentation-11.c: Likewise.


diff --git a/gcc/asan.c b/gcc/asan.c
index 2a61a82..a9eb9aa 100644
--- a/gcc/asan.c
+++ b/gcc/asan.c
@@ -253,9 +253,7 @@  enum asan_check_flags
   ASAN_CHECK_STORE = 1 << 0,
   ASAN_CHECK_SCALAR_ACCESS = 1 << 1,
   ASAN_CHECK_NON_ZERO_LEN = 1 << 2,
-  ASAN_CHECK_START_INSTRUMENTED = 1 << 3,
-  ASAN_CHECK_END_INSTRUMENTED = 1 << 4,
-  ASAN_CHECK_LAST
+  ASAN_CHECK_LAST = 1 << 3
 };
 
 /* Hashtable support for memory references used by gimple
@@ -352,10 +350,7 @@  struct asan_mem_ref_hasher
 inline hashval_t
 asan_mem_ref_hasher::hash (const asan_mem_ref *mem_ref)
 {
-  inchash::hash hstate;
-  inchash::add_expr (mem_ref->start, hstate);
-  hstate.add_wide_int (mem_ref->access_size);
-  return hstate.end ();
+  return iterative_hash_expr (mem_ref->start, 0);
 }
 
 /* Compare two memory references.  We accept the length of either
@@ -365,8 +360,7 @@  inline bool
 asan_mem_ref_hasher::equal (const asan_mem_ref *m1,
 			    const asan_mem_ref *m2)
 {
-  return (m1->access_size == m2->access_size
-	  && operand_equal_p (m1->start, m2->start, 0));
+  return operand_equal_p (m1->start, m2->start, 0);
 }
 
 static hash_table<asan_mem_ref_hasher> *asan_mem_ref_ht;
@@ -417,7 +411,8 @@  has_mem_ref_been_instrumented (tree ref, HOST_WIDE_INT access_size)
   asan_mem_ref r;
   asan_mem_ref_init (&r, ref, access_size);
 
-  return (get_mem_ref_hash_table ()->find (&r) != NULL);
+  asan_mem_ref *saved_ref = get_mem_ref_hash_table ()->find (&r);
+  return saved_ref && saved_ref->access_size >= access_size;
 }
 
 /* Return true iff the memory reference REF has been instrumented.  */
@@ -434,19 +429,11 @@  has_mem_ref_been_instrumented (const asan_mem_ref *ref)
 static bool
 has_mem_ref_been_instrumented (const asan_mem_ref *ref, tree len)
 {
-  /* First let's see if the address of the beginning of REF has been
-     instrumented.  */
-  if (!has_mem_ref_been_instrumented (ref))
-    return false;
+  HOST_WIDE_INT size_in_bytes
+    = tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
 
-  if (len != 0)
-    {
-      /* Let's see if the end of the region has been instrumented.  */
-      if (!has_mem_ref_been_instrumented (asan_mem_ref_get_end (ref, len),
-					  ref->access_size))
-	return false;
-    }
-  return true;
+  return size_in_bytes != -1
+    && has_mem_ref_been_instrumented (ref->start, size_in_bytes);
 }
 
 /* Set REF to the memory reference present in a gimple assignment
@@ -492,7 +479,8 @@  get_mem_refs_of_builtin_call (const gimple call,
 			      asan_mem_ref *dst,
 			      tree *dst_len,
 			      bool *dst_is_store,
-			      bool *dest_is_deref)
+			      bool *dest_is_deref,
+			      bool *intercepted_p)
 {
   gcc_checking_assert (gimple_call_builtin_p (call, BUILT_IN_NORMAL));
 
@@ -506,6 +494,7 @@  get_mem_refs_of_builtin_call (const gimple call,
     {
       /* (s, s, n) style memops.  */
     case BUILT_IN_BCMP:
+      *intercepted_p = false;
     case BUILT_IN_MEMCMP:
       source0 = gimple_call_arg (call, 0);
       source1 = gimple_call_arg (call, 1);
@@ -514,18 +503,20 @@  get_mem_refs_of_builtin_call (const gimple call,
 
       /* (src, dest, n) style memops.  */
     case BUILT_IN_BCOPY:
+      *intercepted_p = false;
       source0 = gimple_call_arg (call, 0);
       dest = gimple_call_arg (call, 1);
       len = gimple_call_arg (call, 2);
       break;
 
       /* (dest, src, n) style memops.  */
-    case BUILT_IN_MEMCPY:
     case BUILT_IN_MEMCPY_CHK:
-    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMPCPY_CHK:
     case BUILT_IN_MEMMOVE_CHK:
     case BUILT_IN_MEMPCPY:
-    case BUILT_IN_MEMPCPY_CHK:
+      *intercepted_p = false;
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMMOVE:
       dest = gimple_call_arg (call, 0);
       source0 = gimple_call_arg (call, 1);
       len = gimple_call_arg (call, 2);
@@ -533,13 +524,15 @@  get_mem_refs_of_builtin_call (const gimple call,
 
       /* (dest, n) style memops.  */
     case BUILT_IN_BZERO:
+      *intercepted_p = false;
       dest = gimple_call_arg (call, 0);
       len = gimple_call_arg (call, 1);
       break;
 
       /* (dest, x, n) style memops*/
-    case BUILT_IN_MEMSET:
     case BUILT_IN_MEMSET_CHK:
+      *intercepted_p = false;
+    case BUILT_IN_MEMSET:
       dest = gimple_call_arg (call, 0);
       len = gimple_call_arg (call, 2);
       break;
@@ -759,6 +752,7 @@  get_mem_refs_of_builtin_call (const gimple call,
 	  gcc_unreachable ();
 
 	access_size = int_size_in_bytes (TREE_TYPE (dest));
+	*intercepted_p = false;
       }
 
     default:
@@ -834,12 +828,12 @@  has_stmt_been_instrumented_p (gimple stmt)
 
       tree src0_len = NULL_TREE, src1_len = NULL_TREE, dest_len = NULL_TREE;
       bool src0_is_store = false, src1_is_store = false,
-	dest_is_store = false, dest_is_deref = false;
+	dest_is_store = false, dest_is_deref = false, intercepted_p = true;
       if (get_mem_refs_of_builtin_call (stmt,
 					&src0, &src0_len, &src0_is_store,
 					&src1, &src1_len, &src1_is_store,
 					&dest, &dest_len, &dest_is_store,
-					&dest_is_deref))
+					&dest_is_deref, &intercepted_p))
 	{
 	  if (src0.start != NULL_TREE
 	      && !has_mem_ref_been_instrumented (&src0, src0_len))
@@ -870,7 +864,7 @@  update_mem_ref_hash_table (tree ref, HOST_WIDE_INT access_size)
   asan_mem_ref_init (&r, ref, access_size);
 
   asan_mem_ref **slot = ht->find_slot (&r, INSERT);
-  if (*slot == NULL)
+  if (*slot == NULL || (*slot)->access_size < access_size)
     *slot = asan_mem_ref_new (ref, access_size);
 }
 
@@ -1608,22 +1602,13 @@  static void
 build_check_stmt (location_t loc, tree base, tree len,
 		  HOST_WIDE_INT size_in_bytes, gimple_stmt_iterator *iter,
 		  bool is_non_zero_len, bool before_p, bool is_store,
-		  bool is_scalar_access, unsigned int align = 0,
-		  bool start_instrumented = false,
-		  bool end_instrumented = false)
+		  bool is_scalar_access, unsigned int align = 0)
 {
   gimple_stmt_iterator gsi = *iter;
   gimple g;
 
   gcc_assert (!(size_in_bytes > 0 && !is_non_zero_len));
 
-  if (start_instrumented && end_instrumented)
-    {
-      if (!before_p)
-	gsi_next (iter);
-      return;
-    }
-
   gsi = *iter;
 
   base = unshare_expr (base);
@@ -1666,10 +1651,6 @@  build_check_stmt (location_t loc, tree base, tree len,
     flags |= ASAN_CHECK_NON_ZERO_LEN;
   if (is_scalar_access)
     flags |= ASAN_CHECK_SCALAR_ACCESS;
-  if (start_instrumented)
-    flags |= ASAN_CHECK_START_INSTRUMENTED;
-  if (end_instrumented)
-    flags |= ASAN_CHECK_END_INSTRUMENTED;
 
   g = gimple_build_call_internal (IFN_ASAN_CHECK, 4,
 				  build_int_cst (integer_type_node, flags),
@@ -1784,6 +1765,22 @@  instrument_derefs (gimple_stmt_iterator *iter, tree t,
 
 }
 
+/*  Insert a memory reference into the hash table if access length
+    can be determined in compile time.  */
+
+static void
+maybe_update_mem_ref_hash_table (tree base, tree len)
+{
+  if (!POINTER_TYPE_P (TREE_TYPE (base))
+      || !INTEGRAL_TYPE_P (TREE_TYPE (len)))
+    return;
+
+  HOST_WIDE_INT size_in_bytes = tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
+
+  if (size_in_bytes != -1)
+    update_mem_ref_hash_table (base, size_in_bytes);
+}
+
 /* Instrument an access to a contiguous memory region that starts at
    the address pointed to by BASE, over a length of LEN (expressed in
    the sizeof (*BASE) bytes).  ITER points to the instruction before
@@ -1802,97 +1799,20 @@  instrument_mem_region_access (tree base, tree len,
       || integer_zerop (len))
     return;
 
-  /* If the beginning of the memory region has already been
-     instrumented, do not instrument it.  */
-  bool start_instrumented = has_mem_ref_been_instrumented (base, 1);
-
-  /* If the end of the memory region has already been instrumented, do
-     not instrument it.  */
-  tree end = asan_mem_ref_get_end (base, len);
-  bool end_instrumented = has_mem_ref_been_instrumented (end, 1);
-
   HOST_WIDE_INT size_in_bytes = tree_fits_shwi_p (len) ? tree_to_shwi (len) : -1;
 
-  build_check_stmt (location, base, len, size_in_bytes, iter,
-		    /*is_non_zero_len*/size_in_bytes > 0, /*before_p*/true,
-		    is_store, /*is_scalar_access*/false, /*align*/0,
-		    start_instrumented, end_instrumented);
-
-  update_mem_ref_hash_table (base, 1);
-  if (size_in_bytes != -1)
-    update_mem_ref_hash_table (end, 1);
+  if ((size_in_bytes == -1)
+      || !has_mem_ref_been_instrumented (base, size_in_bytes))
+    {
+      build_check_stmt (location, base, len, size_in_bytes, iter,
+			/*is_non_zero_len*/size_in_bytes > 0, /*before_p*/true,
+			is_store, /*is_scalar_access*/false, /*align*/0);
+    }
 
+  maybe_update_mem_ref_hash_table (base, len);
   *iter = gsi_for_stmt (gsi_stmt (*iter));
 }
 
-/* Instrument the call (to the builtin strlen function) pointed to by
-   ITER.
-
-   This function instruments the access to the first byte of the
-   argument, right before the call.  After the call it instruments the
-   access to the last byte of the argument; it uses the result of the
-   call to deduce the offset of that last byte.
-
-   Upon completion, iff the call has actually been instrumented, this
-   function returns TRUE and *ITER points to the statement logically
-   following the built-in strlen function call *ITER was initially
-   pointing to.  Otherwise, the function returns FALSE and *ITER
-   remains unchanged.  */
-
-static bool
-instrument_strlen_call (gimple_stmt_iterator *iter)
-{
-  gimple g;
-  gimple call = gsi_stmt (*iter);
-  gcc_assert (is_gimple_call (call));
-
-  tree callee = gimple_call_fndecl (call);
-  gcc_assert (is_builtin_fn (callee)
-	      && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL
-	      && DECL_FUNCTION_CODE (callee) == BUILT_IN_STRLEN);
-
-  location_t loc = gimple_location (call);
-
-  tree len = gimple_call_lhs (call);
-  if (len == NULL)
-    /* Some passes might clear the return value of the strlen call;
-       bail out in that case.  Return FALSE as we are not advancing
-       *ITER.  */
-    return false;
-  gcc_assert (INTEGRAL_TYPE_P (TREE_TYPE (len)));
-
-  len = maybe_cast_to_ptrmode (loc, len, iter, /*before_p*/false);
-
-  tree str_arg = gimple_call_arg (call, 0);
-  bool start_instrumented = has_mem_ref_been_instrumented (str_arg, 1);
-
-  tree cptr_type = build_pointer_type (char_type_node);
-  g = gimple_build_assign_with_ops (NOP_EXPR,
-				    make_ssa_name (cptr_type, NULL),
-				    str_arg, NULL);
-  gimple_set_location (g, loc);
-  gsi_insert_before (iter, g, GSI_SAME_STMT);
-  str_arg = gimple_assign_lhs (g);
-
-  build_check_stmt (loc, str_arg, NULL_TREE, 1, iter,
-		    /*is_non_zero_len*/true, /*before_p=*/true,
-		    /*is_store=*/false, /*is_scalar_access*/true, /*align*/0,
-		    start_instrumented, start_instrumented);
-
-  g = gimple_build_assign_with_ops (POINTER_PLUS_EXPR,
-				    make_ssa_name (cptr_type, NULL),
-				    str_arg,
-				    len);
-  gimple_set_location (g, loc);
-  gsi_insert_after (iter, g, GSI_NEW_STMT);
-
-  build_check_stmt (loc, gimple_assign_lhs (g), NULL_TREE, 1, iter,
-		    /*is_non_zero_len*/true, /*before_p=*/false,
-		    /*is_store=*/false, /*is_scalar_access*/true, /*align*/0);
-
-  return true;
-}
-
 /* Instrument the call to a built-in memory access function that is
    pointed to by the iterator ITER.
 
@@ -1910,49 +1830,54 @@  instrument_builtin_call (gimple_stmt_iterator *iter)
 
   gcc_checking_assert (gimple_call_builtin_p (call, BUILT_IN_NORMAL));
 
-  tree callee = gimple_call_fndecl (call);
   location_t loc = gimple_location (call);
 
-  if (DECL_FUNCTION_CODE (callee) == BUILT_IN_STRLEN)
-    iter_advanced_p = instrument_strlen_call (iter);
-  else
-    {
-      asan_mem_ref src0, src1, dest;
-      asan_mem_ref_init (&src0, NULL, 1);
-      asan_mem_ref_init (&src1, NULL, 1);
-      asan_mem_ref_init (&dest, NULL, 1);
+  asan_mem_ref src0, src1, dest;
+  asan_mem_ref_init (&src0, NULL, 1);
+  asan_mem_ref_init (&src1, NULL, 1);
+  asan_mem_ref_init (&dest, NULL, 1);
 
-      tree src0_len = NULL_TREE, src1_len = NULL_TREE, dest_len = NULL_TREE;
-      bool src0_is_store = false, src1_is_store = false,
-	dest_is_store = false, dest_is_deref = false;
+  tree src0_len = NULL_TREE, src1_len = NULL_TREE, dest_len = NULL_TREE;
+  bool src0_is_store = false, src1_is_store = false, dest_is_store = false,
+    dest_is_deref = false, intercepted_p = true;
 
-      if (get_mem_refs_of_builtin_call (call,
-					&src0, &src0_len, &src0_is_store,
-					&src1, &src1_len, &src1_is_store,
-					&dest, &dest_len, &dest_is_store,
-					&dest_is_deref))
+  if (get_mem_refs_of_builtin_call (call,
+				    &src0, &src0_len, &src0_is_store,
+				    &src1, &src1_len, &src1_is_store,
+				    &dest, &dest_len, &dest_is_store,
+				    &dest_is_deref, &intercepted_p))
+    {
+      if (dest_is_deref)
 	{
-	  if (dest_is_deref)
-	    {
-	      instrument_derefs (iter, dest.start, loc, dest_is_store);
-	      gsi_next (iter);
-	      iter_advanced_p = true;
-	    }
-	  else if (src0_len || src1_len || dest_len)
-	    {
-	      if (src0.start != NULL_TREE)
-		instrument_mem_region_access (src0.start, src0_len,
-					      iter, loc, /*is_store=*/false);
-	      if (src1.start != NULL_TREE)
-		instrument_mem_region_access (src1.start, src1_len,
-					      iter, loc, /*is_store=*/false);
-	      if (dest.start != NULL_TREE)
-		instrument_mem_region_access (dest.start, dest_len,
-					      iter, loc, /*is_store=*/true);
-	      *iter = gsi_for_stmt (call);
-	      gsi_next (iter);
-	      iter_advanced_p = true;
-	    }
+	  instrument_derefs (iter, dest.start, loc, dest_is_store);
+	  gsi_next (iter);
+	  iter_advanced_p = true;
+	}
+      else if (!intercepted_p
+	       && (src0_len || src1_len || dest_len))
+	{
+	  if (src0.start != NULL_TREE)
+	    instrument_mem_region_access (src0.start, src0_len,
+					  iter, loc, /*is_store=*/false);
+	  if (src1.start != NULL_TREE)
+	    instrument_mem_region_access (src1.start, src1_len,
+					  iter, loc, /*is_store=*/false);
+	  if (dest.start != NULL_TREE)
+	    instrument_mem_region_access (dest.start, dest_len,
+					  iter, loc, /*is_store=*/true);
+
+	  *iter = gsi_for_stmt (call);
+	  gsi_next (iter);
+	  iter_advanced_p = true;
+	}
+      else
+	{
+	  if (src0.start != NULL_TREE)
+	    maybe_update_mem_ref_hash_table (src0.start, src0_len);
+	  if (src1.start != NULL_TREE)
+	    maybe_update_mem_ref_hash_table (src1.start, src1_len);
+	  if (dest.start != NULL_TREE)
+	    maybe_update_mem_ref_hash_table (dest.start, dest_len);
 	}
     }
   return iter_advanced_p;
@@ -2507,8 +2432,6 @@  asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
   bool is_scalar_access = (flags & ASAN_CHECK_SCALAR_ACCESS) != 0;
   bool is_store = (flags & ASAN_CHECK_STORE) != 0;
   bool is_non_zero_len = (flags & ASAN_CHECK_NON_ZERO_LEN) != 0;
-  bool start_instrumented = (flags & ASAN_CHECK_START_INSTRUMENTED) != 0;
-  bool end_instrumented = (flags & ASAN_CHECK_END_INSTRUMENTED) != 0;
 
   tree base = gimple_call_arg (g, 1);
   tree len = gimple_call_arg (g, 2);
@@ -2613,46 +2536,42 @@  asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
   else
     {
       /* Slow path for 1, 2 and 4 byte accesses.  */
-
-      if (!start_instrumented)
+      /* Test (shadow != 0)
+	 & ((base_addr & 7) + (real_size_in_bytes - 1)) >= shadow).  */
+      tree shadow = build_shadow_mem_access (&gsi, loc, base_addr,
+					     shadow_ptr_type);
+      gimple shadow_test = build_assign (NE_EXPR, shadow, 0);
+      gimple_seq seq = NULL;
+      gimple_seq_add_stmt (&seq, shadow_test);
+      /* Aligned (>= 8 bytes) can test just
+	 (real_size_in_bytes - 1 >= shadow), as base_addr & 7 is known
+	 to be 0.  */
+      if (align < 8)
 	{
-	  /* Test (shadow != 0)
-	     & ((base_addr & 7) + (real_size_in_bytes - 1)) >= shadow).  */
-	  tree shadow = build_shadow_mem_access (&gsi, loc, base_addr,
-						 shadow_ptr_type);
-	  gimple shadow_test = build_assign (NE_EXPR, shadow, 0);
-	  gimple_seq seq = NULL;
-	  gimple_seq_add_stmt (&seq, shadow_test);
-	  /* Aligned (>= 8 bytes) can test just
-	     (real_size_in_bytes - 1 >= shadow), as base_addr & 7 is known
-	     to be 0.  */
-	  if (align < 8)
-	    {
-	      gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR,
-						       base_addr, 7));
-	      gimple_seq_add_stmt (&seq,
-				   build_type_cast (shadow_type,
-						    gimple_seq_last (seq)));
-	      if (real_size_in_bytes > 1)
-		gimple_seq_add_stmt (&seq,
-				     build_assign (PLUS_EXPR,
-						   gimple_seq_last (seq),
-						   real_size_in_bytes - 1));
-	      t = gimple_assign_lhs (gimple_seq_last_stmt (seq));
-	    }
-	  else
-	    t = build_int_cst (shadow_type, real_size_in_bytes - 1);
-	  gimple_seq_add_stmt (&seq, build_assign (GE_EXPR, t, shadow));
-	  gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, shadow_test,
-						   gimple_seq_last (seq)));
-	  t = gimple_assign_lhs (gimple_seq_last (seq));
-	  gimple_seq_set_location (seq, loc);
-	  gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING);
+	  gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR,
+						   base_addr, 7));
+	  gimple_seq_add_stmt (&seq,
+			       build_type_cast (shadow_type,
+						gimple_seq_last (seq)));
+	  if (real_size_in_bytes > 1)
+	    gimple_seq_add_stmt (&seq,
+				 build_assign (PLUS_EXPR,
+					       gimple_seq_last (seq),
+					       real_size_in_bytes - 1));
+	  t = gimple_assign_lhs (gimple_seq_last_stmt (seq));
 	}
+      else
+	t = build_int_cst (shadow_type, real_size_in_bytes - 1);
+      gimple_seq_add_stmt (&seq, build_assign (GE_EXPR, t, shadow));
+      gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, shadow_test,
+					       gimple_seq_last (seq)));
+      t = gimple_assign_lhs (gimple_seq_last (seq));
+      gimple_seq_set_location (seq, loc);
+      gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING);
 
       /* For non-constant, misaligned or otherwise weird access sizes,
-	 check first and last byte.  */
-      if (size_in_bytes == -1 && !end_instrumented)
+       check first and last byte.  */
+      if (size_in_bytes == -1)
 	{
 	  g = gimple_build_assign_with_ops (MINUS_EXPR,
 					    make_ssa_name (pointer_sized_int_node, NULL),
@@ -2683,9 +2602,8 @@  asan_expand_check_ifn (gimple_stmt_iterator *iter, bool use_calls)
 						   shadow));
 	  gimple_seq_add_stmt (&seq, build_assign (BIT_AND_EXPR, shadow_test,
 						   gimple_seq_last (seq)));
-	  if (!start_instrumented)
-	    gimple_seq_add_stmt (&seq, build_assign (BIT_IOR_EXPR, t,
-						     gimple_seq_last (seq)));
+	  gimple_seq_add_stmt (&seq, build_assign (BIT_IOR_EXPR, t,
+						   gimple_seq_last (seq)));
 	  t = gimple_assign_lhs (gimple_seq_last (seq));
 	  gimple_seq_set_location (seq, loc);
 	  gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING);
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 975f696..530b5a4 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -5762,6 +5762,37 @@  expand_builtin (tree exp, rtx target, rtx subtarget, enum machine_mode mode,
   enum machine_mode target_mode = TYPE_MODE (TREE_TYPE (exp));
   int flags;
 
+  /* When ASan is enabled, we don't want to expand some memory/string
+     builtins and rely on libsanitizer's hooks.  This allows us to avoid
+     redundant checks and be sure, that possible overflow will be detected
+     by ASan.  */
+
+  if (flag_sanitize & SANITIZE_ADDRESS)
+    switch (fcode)
+      {
+      case BUILT_IN_INDEX:
+      case BUILT_IN_MEMCHR:
+      case BUILT_IN_MEMCMP:
+      case BUILT_IN_MEMCPY:
+      case BUILT_IN_MEMMOVE:
+      case BUILT_IN_MEMSET:
+      case BUILT_IN_STRCASECMP:
+      case BUILT_IN_STRCAT:
+      case BUILT_IN_STRCHR:
+      case BUILT_IN_STRCMP:
+      case BUILT_IN_STRCPY:
+      case BUILT_IN_STRDUP:
+      case BUILT_IN_STRLEN:
+      case BUILT_IN_STRNCASECMP:
+      case BUILT_IN_STRNCAT:
+      case BUILT_IN_STRNCMP:
+      case BUILT_IN_STRNCPY:
+	return expand_call (exp, target, ignore);
+
+      default:
+	break;
+      }
+
   if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
     return targetm.expand_builtin (exp, target, subtarget, mode, ignore);
 
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c
index 028f8d7..baacb1e 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-1.c
@@ -6,7 +6,7 @@ 
 /* { dg-do compile } */
 /* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
 
-extern char tab[4];
+extern char tab[6];
 
 static int
 test0 ()
@@ -35,17 +35,10 @@  test1 (int i)
     the initialization.  */
   foo[i] = 1;
 
-  /*__builtin___asan_report_store_n called once here to instrument
-    the store to the memory region of tab.  */
+  /* Instrument tab memory region.  */
   __builtin_memset (tab, 3, sizeof (tab));
 
-  /* There is no instrumentation for the two memset calls below.  */
-  __builtin_memset (tab, 4, sizeof (tab));
-  __builtin_memset (tab, 5, sizeof (tab));
-
-  /* There is a call to __builtin___asan_report_store_n and a call
-     to __builtin___asan_report_load_n to instrument the store to
-     (subset of) the memory region of tab.  */
+  /* Instrument tab[1] with access size 3.  */
   __builtin_memcpy (&tab[1], foo + i, 3);
 
   /* This should not generate a __builtin___asan_report_load1 because
@@ -63,6 +56,5 @@  main ()
 }
 
 /* { dg-final { scan-tree-dump-times "__builtin___asan_report_store1" 3 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 2 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load" 1 "sanopt" }  } */
+/* { dg-final { scan-tree-dump-not "__builtin___asan_report_load1" "sanopt" } } */
 /* { dg-final { cleanup-tree-dump "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-10.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-10.c
new file mode 100644
index 0000000..24dfcfe
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-10.c
@@ -0,0 +1,18 @@ 
+/* { dg-options "-fdump-tree-sanopt" } */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+extern __UINT32_TYPE__ a;
+
+void
+foo ()
+{
+  /* Instrument a with access size 3.  */
+  int d = __builtin_memcmp (&a, "123", 3);
+  /* This should  generate a __builtin___asan_report_store4, because
+     the reference to a has been instrumented above with access size 3.  */
+  a = 1;
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store4" 1 "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-11.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-11.c
new file mode 100644
index 0000000..4082f32
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-11.c
@@ -0,0 +1,20 @@ 
+/* { dg-options "-fdump-tree-sanopt" } */
+/* { dg-do compile } */
+/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
+
+extern __UINT32_TYPE__ a;
+
+void
+foo ()
+{
+  /* Instrument a with access size 5.  */
+  int d = __builtin_memcmp (&a, "12345", 4);
+  /* This should not generate a __builtin___asan_report_store4 because
+     the reference to a has been already instrumented above with access
+     size 5.  */
+  a = 1;
+}
+
+/* { dg-final { scan-tree-dump-not "& 7" "sanopt" } } */
+/* { dg-final { scan-tree-dump-not "__builtin___asan_report_store" "sanopt" } } */
+/* { dg-final { cleanup-tree-dump "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-2.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-2.c
deleted file mode 100644
index a58411c..0000000
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-2.c
+++ /dev/null
@@ -1,26 +0,0 @@ 
-/* This tests that when faced with two references to the same memory
-   location in the same basic block, the second reference should not
-   be instrumented by the Address Sanitizer.  But in case of access to
-   overlapping regions we must be precise.  */
-
-/* { dg-options "-fdump-tree-sanopt" } */
-/* { dg-do compile } */
-/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
-
-int
-main ()
-{
-  char tab[5];
-
-  /* Here, we instrument the access at offset 0 and access at offset
-     4.  */
-  __builtin_memset (tab, 1, sizeof (tab));
-  /* We instrumented access at offset 0 above already, so only access
-     at offset 3 is instrumented.  */
-  __builtin_memset (tab, 1, 3);
-}
-
-/* { dg-final { scan-tree-dump-times "& 7" 3 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 2 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report" 2 "sanopt" }  } */
-/* { dg-final { cleanup-tree-dump "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c
index c3632aa..a613b92 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-4.c
@@ -5,13 +5,13 @@ 
 void
 foo  (int *a, char *b, char *c)
 {
-  /* One check for c[0], one check for a[], one check for c, two checks for b.  */
+  /* One check for c[0], one check for a[].  */
   __builtin_memmove (c, b, a[c[0]]);
-  /* For a total of 5 checks.  */
+  /* For a total of 2 checks.  */
+  int d = c[0] == 1;
 }
 
-/* { dg-final { scan-tree-dump-times "& 7" 5 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 2 "sanopt" } } */
 /* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 1 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 1 "sanopt" } } */
 /* { dg-final { cleanup-tree-dump "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c
index 077ea34..f4ca603 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-5.c
@@ -5,14 +5,12 @@ 
 void
 foo  (int *a, char *b, char *c)
 {
-  /* One check for b[0], one check for a[], 2 checks for c and one checks for b.  */
-  __builtin_memmove (c, b, a[b[0]]);
-  /* For a total of 5 checks.  */
+  /* One check for a[].  */
+  __builtin_memmove (c, b, a[0]);
+  /* For a total of 1 checks.  */
+  int d = a[0] == 0;
 }
 
-/* { dg-final { scan-tree-dump-times "& 7" 5 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 1 "sanopt" } } */
 /* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 1 "sanopt" } } */
 /* { dg-final { cleanup-tree-dump "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c
index 6d87104..757f0ee 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-6.c
@@ -5,16 +5,15 @@ 
 void
 foo  (int *a, char *b, char *c)
 {
-  /* One check for c[0], one check for a[], one check for c and 2 checks for b.  */
+  /* One check for c[0], one check for a[].  */
   __builtin_memmove (c, b, a[c[0]]);
-  /* One check for a[], one check for c and one check for b.  */
+  /* One check for b[0], one check for a[].  */
   __builtin_memmove (c, b, a[b[0]]);
-  /* For a total of 8 checks.  */
+  /* For a total of 4 checks.  */
+  int d = c[0] == b[0];
 }
 
-/* { dg-final { scan-tree-dump-times "& 7" 8 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 4 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 2 "sanopt" } } */
 /* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 2 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 2 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 2 "sanopt" } } */
 /* { dg-final { cleanup-tree-dump "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c
index 5baa10d..0c50145 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-7.c
@@ -2,26 +2,22 @@ 
 /* { dg-do compile } */
 /* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
 
-char e[200];
+char e[5];
 
-struct S
+extern struct S
 {
-  char a[100];
-  char b[100];
+  int a;
+  char b;
 } s;
 
 int
 foo  (int *a, char *b, char *c)
 {
-  /* 2 checks for s.a, 2 checks for e.  */
-  int d = __builtin_memcmp (s.a, e, 100);
-  /* One check for s.a and one check for e.  */
-  d += __builtin_memcmp (s.a, e, 200);
-  /* For a total of 6 checks.  */
-  return d;
+  int d = __builtin_memcmp (&s.a, e, 4);
+  /* No check because s.a was instrumented above with access size 4.  */
+  return s.a;
 }
 
-/* { dg-final { scan-tree-dump-times "& 7" 6 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 4 "sanopt" } } */
-/* { dg-final { scan-tree-dump-not "__builtin___asan_report_store" "sanopt" } } */
+/* { dg-final { scan-tree-dump-not "& 7" "sanopt" } } */
+/* { dg-final { scan-tree-dump-not "__builtin___asan_report_load4" "sanopt" } } */
 /* { dg-final { cleanup-tree-dump "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c
index 2a4c081..4eeedce 100644
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c
+++ b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-8.c
@@ -5,16 +5,16 @@ 
 char
 foo  (int *a, char *b, char *c)
 {
-  /* One check for b[0], one check for a[], two checks for c and one check for b.  */
+  /* One check for b[0], one check for a[].  */
   __builtin_memmove (c, b, a[b[0]]);
+  /* One check for c[0], one check for a[].  */
+  __builtin_memmove (b, c, a[c[0]]);
   /* No checks here.  */
   return c[0] + b[0];
-  /* For a total of 5 checks.  */
+  /* For a total of 4 checks.  */
 }
 
-/* { dg-final { scan-tree-dump-times "& 7" 5 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load_n" 1 "sanopt" } } */
-/* { dg-final { scan-tree-dump-times "__builtin___asan_report_store_n" 1 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "& 7" 4 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load1" 2 "sanopt" } } */
+/* { dg-final { scan-tree-dump-times "__builtin___asan_report_load4" 2 "sanopt" } } */
 /* { dg-final { cleanup-tree-dump "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-9.c b/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-9.c
deleted file mode 100644
index 9449de5..0000000
--- a/gcc/testsuite/c-c++-common/asan/no-redundant-instrumentation-9.c
+++ /dev/null
@@ -1,13 +0,0 @@ 
-/* { dg-options "-fdump-tree-sanopt" } */
-/* { dg-do compile } */
-/* { dg-skip-if "" { *-*-* } { "*" } { "-O0" } } */
-
-__SIZE_TYPE__
-f (char *a)
-{
-  a[0] = '1';
-  return  __builtin_strlen (a);
-}
-
-/* { dg-final { scan-tree-dump-times "__asan_report_load1" 1 "sanopt" } } */
-/* { dg-final { cleanup-tree-dump "sanopt" } } */
diff --git a/gcc/testsuite/c-c++-common/asan/strlen-overflow-1.c b/gcc/testsuite/c-c++-common/asan/strlen-overflow-1.c
index f58f554..0f49286 100644
--- a/gcc/testsuite/c-c++-common/asan/strlen-overflow-1.c
+++ b/gcc/testsuite/c-c++-common/asan/strlen-overflow-1.c
@@ -9,14 +9,8 @@  char a[2] = "0";
 #ifdef __cplusplus
 extern "C"
 #endif
-
-__attribute__((no_sanitize_address, noinline)) __SIZE_TYPE__
-strlen (const char *p) {
-
-  __SIZE_TYPE__ n = 0;
-  for (; *p; ++n, ++p);
-  return n;
-}
+__SIZE_TYPE__
+strlen (const char *p);
 
 int main () {
   char *p = &a[0];
@@ -25,6 +19,6 @@  int main () {
   return __builtin_strlen (a);
 }
 
-/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+ thread T0.*(\n|\r\n|\r)" } */
-/* { dg-output "    #0 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*strlen-overflow-1.c:25|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
-/* { dg-output "\[^\n\r]*0x\[0-9a-f\]+ is located 1 bytes inside of global variable" } */
+/* { dg-output "READ of size 2 at 0x\[0-9a-f\]+ thread T0.*(\n|\r\n|\r)" } */
+/* { dg-output "    #1 0x\[0-9a-f\]+ (in _*main (\[^\n\r]*strlen-overflow-1.c:19|\[^\n\r]*:0)|\[(\]).*(\n|\r\n|\r)" } */
+/* { dg-output "\[^\n\r]*0x\[0-9a-f\]+ is located 0 bytes to the right of global variable" } */