diff --git a/compiler-rt/lib/asan/asan_errors.cpp b/compiler-rt/lib/asan/asan_errors.cpp
index 2a207cd06ccac..075efec081b53 100644
--- a/compiler-rt/lib/asan/asan_errors.cpp
+++ b/compiler-rt/lib/asan/asan_errors.cpp
@@ -437,8 +437,16 @@ ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr,
     bug_descr = "unknown-crash";
     if (AddrIsInMem(addr)) {
       u8 *shadow_addr = (u8 *)MemToShadow(addr);
-      // If we are accessing 16 bytes, look at the second shadow byte.
-      if (*shadow_addr == 0 && access_size > ASAN_SHADOW_GRANULARITY)
+      u8 *shadow_addr_upper_bound = (u8 *)MEM_TO_SHADOW(addr + access_size);
+      // We use the MEM_TO_SHADOW macro for the upper bound above instead of
+      // MemToShadow to skip the assertion that (addr + access_size) is within
+      // the valid memory range. The validity of the shadow address is checked
+      // via AddrIsInShadow in the while loop below.
+
+      // If the access could span multiple shadow bytes,
+      // do a sequential scan and look for the first bad shadow byte.
+      while (*shadow_addr == 0 && shadow_addr < shadow_addr_upper_bound &&
+             AddrIsInShadow((uptr)(shadow_addr + 1)))
         shadow_addr++;
       // If we are in the partial right redzone, look at the next shadow byte.
       if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++;
diff --git a/compiler-rt/lib/asan/asan_interceptors_memintrinsics.h b/compiler-rt/lib/asan/asan_interceptors_memintrinsics.h
index 14727a5d665ed..7bed604aec678 100644
--- a/compiler-rt/lib/asan/asan_interceptors_memintrinsics.h
+++ b/compiler-rt/lib/asan/asan_interceptors_memintrinsics.h
@@ -52,31 +52,31 @@ struct AsanInterceptorContext {
 // that no extra frames are created, and stack trace contains
 // relevant information only.
 // We check all shadow bytes.
-#define ACCESS_MEMORY_RANGE(ctx, offset, size, isWrite)                   \
-  do {                                                                    \
-    uptr __offset = (uptr)(offset);                                       \
-    uptr __size = (uptr)(size);                                           \
-    uptr __bad = 0;                                                       \
-    if (UNLIKELY(__offset > __offset + __size)) {                         \
-      GET_STACK_TRACE_FATAL_HERE;                                         \
-      ReportStringFunctionSizeOverflow(__offset, __size, &stack);         \
-    }                                                                     \
-    if (UNLIKELY(!QuickCheckForUnpoisonedRegion(__offset, __size)) &&     \
-        (__bad = __asan_region_is_poisoned(__offset, __size))) {          \
-      AsanInterceptorContext *_ctx = (AsanInterceptorContext *)ctx;       \
-      bool suppressed = false;                                            \
-      if (_ctx) {                                                         \
-        suppressed = IsInterceptorSuppressed(_ctx->interceptor_name);     \
-        if (!suppressed && HaveStackTraceBasedSuppressions()) {           \
-          GET_STACK_TRACE_FATAL_HERE;                                     \
-          suppressed = IsStackTraceSuppressed(&stack);                    \
-        }                                                                 \
-      }                                                                   \
-      if (!suppressed) {                                                  \
-        GET_CURRENT_PC_BP_SP;                                             \
-        ReportGenericError(pc, bp, sp, __bad, isWrite, __size, 0, false); \
-      }                                                                   \
-    }                                                                     \
+#define ACCESS_MEMORY_RANGE(ctx, offset, size, isWrite)                      \
+  do {                                                                       \
+    uptr __offset = (uptr)(offset);                                          \
+    uptr __size = (uptr)(size);                                              \
+    uptr __bad = 0;                                                          \
+    if (UNLIKELY(__offset > __offset + __size)) {                            \
+      GET_STACK_TRACE_FATAL_HERE;                                            \
+      ReportStringFunctionSizeOverflow(__offset, __size, &stack);            \
+    }                                                                        \
+    if (UNLIKELY(!QuickCheckForUnpoisonedRegion(__offset, __size)) &&        \
+        (__bad = __asan_region_is_poisoned(__offset, __size))) {             \
+      AsanInterceptorContext *_ctx = (AsanInterceptorContext *)ctx;          \
+      bool suppressed = false;                                               \
+      if (_ctx) {                                                            \
+        suppressed = IsInterceptorSuppressed(_ctx->interceptor_name);        \
+        if (!suppressed && HaveStackTraceBasedSuppressions()) {              \
+          GET_STACK_TRACE_FATAL_HERE;                                        \
+          suppressed = IsStackTraceSuppressed(&stack);                       \
+        }                                                                    \
+      }                                                                      \
+      if (!suppressed) {                                                     \
+        GET_CURRENT_PC_BP_SP;                                                \
+        ReportGenericError(pc, bp, sp, __offset, isWrite, __size, 0, false); \
+      }                                                                      \
+    }                                                                        \
   } while (0)
 
 #define ASAN_READ_RANGE(ctx, offset, size) \
diff --git a/compiler-rt/test/asan/TestCases/heap-overflow-large.cpp b/compiler-rt/test/asan/TestCases/heap-overflow-large-offset.cpp
similarity index 100%
rename from compiler-rt/test/asan/TestCases/heap-overflow-large.cpp
rename to compiler-rt/test/asan/TestCases/heap-overflow-large-offset.cpp
diff --git a/compiler-rt/test/asan/TestCases/wild_pointer.cpp b/compiler-rt/test/asan/TestCases/heap-overflow-large-read.cpp
similarity index 71%
rename from compiler-rt/test/asan/TestCases/wild_pointer.cpp
rename to compiler-rt/test/asan/TestCases/heap-overflow-large-read.cpp
index 8969a285e565e..7671e93d74b0c 100644
--- a/compiler-rt/test/asan/TestCases/wild_pointer.cpp
+++ b/compiler-rt/test/asan/TestCases/heap-overflow-large-read.cpp
@@ -11,7 +11,7 @@
 int main() {
   char *p = new char;
   char *dest = new char;
-  const size_t offset = 0x4567890123456789;
+  const size_t size = 0x4567890123456789;
 
   // The output here needs to match the output from the sanitizer runtime,
   // which includes 0x and prints hex in lower case.
@@ -19,14 +19,14 @@ int main() {
   // On Windows, %p omits %0x and prints hex characters in upper case,
   // so we use PRIxPTR instead of %p.
   fprintf(stderr, "Expected bad addr: %#" PRIxPTR "\n",
-          reinterpret_cast<uintptr_t>(p + offset));
+          reinterpret_cast<uintptr_t>(p));
   // Flush it so the output came out before the asan report.
   fflush(stderr);
 
-  memmove(dest, p, offset);
+  memmove(dest, p, size);
   return 0;
 }
 
 // CHECK: Expected bad addr: [[ADDR:0x[0-9,a-f]+]]
-// CHECK: AddressSanitizer: unknown-crash on address [[ADDR]]
-// CHECK: Address [[ADDR]] is a wild pointer inside of access range of size 0x4567890123456789
+// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR]]
+// CHECK: READ of size 5001116549197948809 at [[ADDR]] thread T0
diff --git a/compiler-rt/test/asan/TestCases/stack-buffer-overflow-partial.cpp b/compiler-rt/test/asan/TestCases/stack-buffer-overflow-partial.cpp
new file mode 100644
index 0000000000000..0e10d673c09c3
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/stack-buffer-overflow-partial.cpp
@@ -0,0 +1,70 @@
+// RUN: %clangxx_asan %s -o %t -DSTACK_ALLOC_SIZE=8 -DREAD_SIZE=8
+// RUN: not %run %t 1 2>&1 | FileCheck %s
+// RUN: not %run %t 2 2>&1 | FileCheck %s
+// RUN: not %run %t 7 2>&1 | FileCheck %s
+
+// RUN: %clangxx_asan %s -o %t -DSTACK_ALLOC_SIZE=16 -DREAD_SIZE=16
+// RUN: not %run %t 1 2>&1 | FileCheck %s
+// RUN: not %run %t 2 2>&1 | FileCheck %s
+// RUN: not %run %t 7 2>&1 | FileCheck %s
+// RUN: not %run %t 8 2>&1 | FileCheck %s
+// RUN: not %run %t 15 2>&1 | FileCheck %s
+
+// RUN: %clangxx_asan %s -o %t -DSTACK_ALLOC_SIZE=16 -DREAD_SIZE=15
+// RUN: not %run %t 2 2>&1 | FileCheck %s
+// RUN: not %run %t 3 2>&1 | FileCheck %s
+// RUN: not %run %t 7 2>&1 | FileCheck %s
+// RUN: not %run %t 8 2>&1 | FileCheck %s
+// RUN: not %run %t 15 2>&1 | FileCheck %s
+
+// RUN: %clangxx_asan %s -o %t -DSTACK_ALLOC_SIZE=20 -DREAD_SIZE=18
+// RUN: not %run %t 3 2>&1 | FileCheck %s
+// RUN: not %run %t 7 2>&1 | FileCheck %s
+// RUN: not %run %t 10 2>&1 | FileCheck %s
+// RUN: not %run %t 13 2>&1 | FileCheck %s
+// RUN: not %run %t 19 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+struct X {
+  char bytes[READ_SIZE];
+};
+
+__attribute__((noinline)) struct X out_of_bounds(int offset) {
+  volatile char bytes[STACK_ALLOC_SIZE];
+  struct X* x_ptr = (struct X*)(bytes + offset);
+  return *x_ptr;
+}
+
+int main(int argc, char **argv) {
+  int offset = atoi(argv[1]);
+
+  // Output READ_SIZE to check against the asan report,
+  // and flush it so the output comes out first.
+  fprintf(stderr, "Expected READ size: %d\n", READ_SIZE);
+  fflush(stderr);
+
+  // We are explicitly testing that we correctly detect and report this error
+  // as a *partial stack buffer overflow*.
+  assert(offset < STACK_ALLOC_SIZE);
+  assert(offset + READ_SIZE > STACK_ALLOC_SIZE);
+
+  struct X x = out_of_bounds(offset);
+  int y = 0;
+
+  for (int i = 0; i < READ_SIZE; i++) {
+    y ^= x.bytes[i];
+  }
+
+  return y;
+}
+
+// CHECK: Expected READ size: [[READ_SIZE:[0-9]+]]
+// CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address
+// CHECK: READ of size [[READ_SIZE]] at {{0x.*}} thread T0
+// CHECK: {{.* 0x.* in out_of_bounds.*stack-buffer-overflow-partial.cpp:}}
+// CHECK: {{Address 0x.* is located in stack of thread T0 at offset}}
+// CHECK: {{    #0 0x.* in out_of_bounds.*stack-buffer-overflow-partial.cpp:}}
+// CHECK: 'bytes'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
diff --git a/compiler-rt/test/asan/TestCases/strcasestr-1.c b/compiler-rt/test/asan/TestCases/strcasestr-1.c
index 9fa4a2b31b97f..a3118f0f6a022 100644
--- a/compiler-rt/test/asan/TestCases/strcasestr-1.c
+++ b/compiler-rt/test/asan/TestCases/strcasestr-1.c
@@ -19,7 +19,7 @@ int main(int argc, char **argv) {
   char s1[4] = "abC";
   __asan_poison_memory_region ((char *)&s1[2], 2);
   r = strcasestr(s1, s2);
-  // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
   assert(r == s1 + 2);
   return 0;
 }
diff --git a/compiler-rt/test/asan/TestCases/strcasestr-2.c b/compiler-rt/test/asan/TestCases/strcasestr-2.c
index 920d11e275c6a..551c081669ea0 100644
--- a/compiler-rt/test/asan/TestCases/strcasestr-2.c
+++ b/compiler-rt/test/asan/TestCases/strcasestr-2.c
@@ -20,6 +20,6 @@ int main(int argc, char **argv) {
   __asan_poison_memory_region ((char *)&s2[2], 2);
   r = strcasestr(s1, s2);
   assert(r == 0);
-  // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
   return 0;
 }
diff --git a/compiler-rt/test/asan/TestCases/strcspn-1.c b/compiler-rt/test/asan/TestCases/strcspn-1.c
index 2a9f7d7fbddfc..f7a7722d5ec0c 100644
--- a/compiler-rt/test/asan/TestCases/strcspn-1.c
+++ b/compiler-rt/test/asan/TestCases/strcspn-1.c
@@ -14,7 +14,7 @@ int main(int argc, char **argv) {
   char s1[4] = "caB";
   __asan_poison_memory_region ((char *)&s1[2], 2);
   r = strcspn(s1, s2);
-  // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
   assert(r == 1);
   return 0;
 }
diff --git a/compiler-rt/test/asan/TestCases/strcspn-2.c b/compiler-rt/test/asan/TestCases/strcspn-2.c
index a51fb911e8025..338da85b8d496 100644
--- a/compiler-rt/test/asan/TestCases/strcspn-2.c
+++ b/compiler-rt/test/asan/TestCases/strcspn-2.c
@@ -14,7 +14,7 @@ int main(int argc, char **argv) {
   char s2[4] = "abc";
   __asan_poison_memory_region ((char *)&s2[2], 2);
   r = strcspn(s1, s2);
-  // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
   assert(r == 0);
   return 0;
 }
diff --git a/compiler-rt/test/asan/TestCases/strpbrk-1.c b/compiler-rt/test/asan/TestCases/strpbrk-1.c
index eb323267828bb..438d4ff288463 100644
--- a/compiler-rt/test/asan/TestCases/strpbrk-1.c
+++ b/compiler-rt/test/asan/TestCases/strpbrk-1.c
@@ -14,7 +14,7 @@ int main(int argc, char **argv) {
   char s1[4] = "cab";
   __asan_poison_memory_region ((char *)&s1[2], 2);
   r = strpbrk(s1, s2);
-  // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
   assert(r == s1 + 1);
   return 0;
 }
diff --git a/compiler-rt/test/asan/TestCases/strpbrk-2.c b/compiler-rt/test/asan/TestCases/strpbrk-2.c
index 1f24656dcb58d..46948b086835a 100644
--- a/compiler-rt/test/asan/TestCases/strpbrk-2.c
+++ b/compiler-rt/test/asan/TestCases/strpbrk-2.c
@@ -14,7 +14,7 @@ int main(int argc, char **argv) {
   char s2[4] = "bca";
   __asan_poison_memory_region ((char *)&s2[2], 2);
   r = strpbrk(s1, s2);
-  // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
   assert(r == s1);
   return 0;
 }
diff --git a/compiler-rt/test/asan/TestCases/strspn-1.c b/compiler-rt/test/asan/TestCases/strspn-1.c
index 5ddb14f507ff5..4265b3df66ec6 100644
--- a/compiler-rt/test/asan/TestCases/strspn-1.c
+++ b/compiler-rt/test/asan/TestCases/strspn-1.c
@@ -14,7 +14,7 @@ int main(int argc, char **argv) {
   char s1[4] = "acb";
   __asan_poison_memory_region ((char *)&s1[2], 2);
   r = strspn(s1, s2);
-  // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
   assert(r == 1);
   return 0;
 }
diff --git a/compiler-rt/test/asan/TestCases/strspn-2.c b/compiler-rt/test/asan/TestCases/strspn-2.c
index d564ef8aefb93..098b66dd3ea48 100644
--- a/compiler-rt/test/asan/TestCases/strspn-2.c
+++ b/compiler-rt/test/asan/TestCases/strspn-2.c
@@ -14,7 +14,7 @@ int main(int argc, char **argv) {
   char s2[5] = "abcd";
   __asan_poison_memory_region ((char *)&s2[3], 2);
   r = strspn(s1, s2);
-  // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
   assert(r >= 2);
   return 0;
 }
diff --git a/compiler-rt/test/asan/TestCases/strstr-1.c b/compiler-rt/test/asan/TestCases/strstr-1.c
index 319cff546d193..23ece04000fc2 100644
--- a/compiler-rt/test/asan/TestCases/strstr-1.c
+++ b/compiler-rt/test/asan/TestCases/strstr-1.c
@@ -15,7 +15,7 @@ int main(int argc, char **argv) {
   char s1[4] = "acb";
   __asan_poison_memory_region ((char *)&s1[2], 2);
   r = strstr(s1, s2);
-  // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} {{partially overflows this variable|is inside this variable}}
+  // CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
   assert(r == s1 + 1);
   return 0;
 }
diff --git a/compiler-rt/test/asan/TestCases/strstr-2.c b/compiler-rt/test/asan/TestCases/strstr-2.c
index 4d00c6e632bbb..caa4543fec07f 100644
--- a/compiler-rt/test/asan/TestCases/strstr-2.c
+++ b/compiler-rt/test/asan/TestCases/strstr-2.c
@@ -15,7 +15,7 @@ int main(int argc, char **argv) {
   char s2[4] = "cab";
   __asan_poison_memory_region ((char *)&s2[2], 2);
   r = strstr(s1, s2);
-  // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
   assert(r == 0);
   return 0;
 }
diff --git a/compiler-rt/test/asan/TestCases/strtok.c b/compiler-rt/test/asan/TestCases/strtok.c
index c7b2617772544..ea92d611e8b1a 100644
--- a/compiler-rt/test/asan/TestCases/strtok.c
+++ b/compiler-rt/test/asan/TestCases/strtok.c
@@ -35,7 +35,7 @@ void test1() {
   char token_delimiter[2] = "b";
   __asan_poison_memory_region ((char *)&token_delimiter[1], 2);
   token = strtok(s, token_delimiter);
-  // CHECK1: 'token_delimiter'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK1: 'token_delimiter'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
 }
 
 // Check that we find overflows in the delimiters on the second call (str == NULL)
@@ -48,7 +48,7 @@ void test2() {
   assert(strcmp(token, "a") == 0);
   __asan_poison_memory_region ((char *)&token_delimiter[1], 2);
   token = strtok(NULL, token_delimiter);
-  // CHECK2: 'token_delimiter'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK2: 'token_delimiter'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
 }
 
 // Check that we find overflows in the string (only on the first call) with strict_string_checks.
@@ -58,7 +58,7 @@ void test3() {
   char token_delimiter[2] = "b";
   __asan_poison_memory_region ((char *)&s[3], 2);
   token = strtok(s, token_delimiter);
-  // CHECK3: 's'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK3: 's'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
 }
 
 // Check that we do not crash when strtok returns NULL with strict_string_checks.
@@ -78,7 +78,7 @@ void test5() {
   __asan_poison_memory_region ((char *)&s[2], 2);
   __asan_poison_memory_region ((char *)&token_delimiter[1], 2);
   token = strtok(s, token_delimiter);
-  // CHECK5: 's'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
+  // CHECK5: 's'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
 }
 
 // Check that we find overflows in the delimiters (only on the first call) with !strict_string_checks.