diff --git a/compiler-rt/lib/asan/asan_posix.cpp b/compiler-rt/lib/asan/asan_posix.cpp
--- a/compiler-rt/lib/asan/asan_posix.cpp
+++ b/compiler-rt/lib/asan/asan_posix.cpp
@@ -24,6 +24,7 @@
 #include "sanitizer_common/sanitizer_procmaps.h"
 
 #include <pthread.h>
+#include <signal.h>
 #include <stdlib.h>
 #include <sys/time.h>
 #include <sys/resource.h>
@@ -37,8 +38,49 @@
   ReportDeadlySignal(sig);
 }
 
+// If current thread is on signal alternate stack, returns alternate stack
+// bounds. Otherwise, returns .have_bounds = false.
+static StackBounds GetStackBoundsSigAlt() {
+  // sigaltstack should not fail in this mode according to its list of possible
+  // errors. However, if you ever encounter a failure, please consider:
+  // If we don't know whether or not we're on the signal alternate stack, we
+  // might continue by assuming that we're NOT on the signal alternate stack.
+  // For __asan_handle_no_return, this means:
+  // - if we read the stack top from cache, our range to unpoison is
+  //   [sigalt stack bottom, default stack top).
+  //   . If sigalt stack is shortly before default stack, we'll unpoison both
+  //     stacks => false negatives
+  //   . otherwise, clean-up size heuristic triggers and we don't unpoison
+  //     anything => false positives, triggers warning
+  // - if we read the stack top from OS
+  //   . if we search for the address of a local variable in the memory
+  //     mappings, we get a correct sigalt stack bottom but the found sigalt
+  //     stack top can be too high (stack is smaller than mapping) or too low
+  //     (stack consists of multiple consecutive mappings, pathological).
+  //     _ if top is too high, we unpoison too much => false negatives
+  //     _ if top is too low, we unpoison too little => false positives
+  //   . using pthread_attr_getstack, we get the default stack bounds. The
+  //     sigalt stack is not unpoisoned => false positives
+  StackBounds bounds;
+  stack_t signal_stack;
+  CHECK(0 == sigaltstack(nullptr, &signal_stack));
+  if (signal_stack.ss_flags == SS_ONSTACK) {
+    bounds.bottom = (uptr)signal_stack.ss_sp;
+    bounds.top = (uptr)((char *)signal_stack.ss_sp + signal_stack.ss_size);
+    bounds.have_bounds = true;
+  } else {
+    bounds.top = bounds.bottom = 0;
+    bounds.have_bounds = false;
+  }
+  return bounds;
+}
+
 StackBounds GetStackBounds(AsanThread* curr_thread) {
-  return GetStackBoundsFromCacheOrOs(curr_thread);
+  const StackBounds sigalt_bounds = GetStackBoundsSigAlt();
+  if(sigalt_bounds.have_bounds)
+    return sigalt_bounds;
+  else
+    return GetStackBoundsFromCacheOrOs(curr_thread);
 }
 
 // ---------------------- TSD ---------------- {{{1
diff --git a/compiler-rt/test/asan/TestCases/unpoison-alternate-stack.cpp b/compiler-rt/test/asan/TestCases/unpoison-alternate-stack.cpp
new file mode 100644
--- /dev/null
+++ b/compiler-rt/test/asan/TestCases/unpoison-alternate-stack.cpp
@@ -0,0 +1,172 @@
+// Tests that __asan_handle_no_return properly unpoisons the signal alternate
+// stack.
+
+// Don't optimize, otherwise the variables which create redzones might be
+// dropped.
+// RUN: %clangxx_asan -fexceptions -O0 %s -o %t -pthread
+// RUN: %run %t
+
+#include <cassert>
+#include <cerrno>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include <sanitizer/asan_interface.h>
+
+#define HANDLE_ERROR_FROM_RETVAL(EXPRESSION) \
+  handleErrorFromRetval((EXPRESSION), #EXPRESSION, __FUNCTION__, __FILE__, __LINE__)
+
+// If EXPRESSION == 0, no error happened.
+#define HANDLE_ERROR_FROM_ERRNO(EXPRESSION) \
+  handleErrorFromErrno((EXPRESSION) != 0, #EXPRESSION, __FUNCTION__, __FILE__, __LINE__)
+
+#define ERROR(STR, ERRNO) \
+  error(ERRNO, STR, __FUNCTION__, __FILE__, __LINE__)
+
+namespace {
+
+void error(int Err, char const *Expression,
+           char const *Function, char const *File, int Line) {
+  std::fprintf(stderr,
+               "Error %d: %s\nIn expression %s\nIn function %s\nIn file %s:%d\nAborting.\n\n",
+               Err, std::strerror(Err), Expression, Function, File, Line);
+  std::abort();
+}
+
+void handleErrorFromRetval(int Err, char const *Expression,
+                           char const *Function, char const *File, int Line) {
+  if (Err)
+    error(Err, Expression, Function, File, Line);
+}
+
+void handleErrorFromErrno(bool Failed, char const *Expression,
+                          char const *Function, char const *File, int Line) {
+  if (Failed)
+    error(errno, Expression, Function, File, Line);
+}
+
+char *LeftRedzone;
+char *RightRedzone;
+
+// Create redzones for stack variables in shadow memory and throw,
+// which should unpoison the entire stack.
+void __attribute__((noinline)) poisonStack() {
+  char Blob[100]; // This variable must not be optimized out, because we use it
+                  // to create redzones.
+
+  LeftRedzone = Blob - 1;
+  RightRedzone = Blob + sizeof(Blob);
+
+  assert(__asan_address_is_poisoned(LeftRedzone));
+  assert(__asan_address_is_poisoned(RightRedzone));
+
+  // Trigger stack unwinding, which avoids normal cleanup of redzone markers.
+  // Instead, __asan_handle_no_return is called which unpoisons the entire
+  // stack.
+  throw "up";
+}
+
+// - Create redzones for a stack variable
+// - Unwind the stack
+// - Ensure that the redzones have been removed
+void testHandleNoReturn() {
+  try {
+    poisonStack();
+  } catch (...) {
+    // Ensure that the redzones created by poisonStack have been removed.
+    assert(0 == __asan_region_is_poisoned(LeftRedzone,
+                                          RightRedzone - LeftRedzone));
+  }
+}
+
+// Signal handler which must be run on signal alternate stack,
+// and tests if stack unwinding properly triggers stack unpoisoning.
+void signalHandler(int, siginfo_t *, void *) {
+  stack_t Stack;
+  HANDLE_ERROR_FROM_ERRNO(sigaltstack(nullptr, &Stack));
+  assert(Stack.ss_flags == SS_ONSTACK);
+
+  // second test on alt stack (actual check)
+  testHandleNoReturn();
+}
+
+// Main test function.
+// Must be run on another thread to be able to control memory placement between
+// default stack and alternate signal stack.
+// If the alternate signal stack is placed in close proximity before the
+// default stack, __asan_handle_no_return might unpoison both, even without
+// being aware of the signal alternate stack.
+// We want to test reliable that __asan_handle_no_return can properly unpoison
+// the signal alternate stack.
+void *threadFun(void *AltStack) {
+  // first test on default stack (sanity check)
+  testHandleNoReturn();
+
+  HANDLE_ERROR_FROM_ERRNO(sigaltstack((stack_t const *)AltStack, nullptr));
+
+  struct sigaction Action = {
+      .sa_sigaction = signalHandler,
+      .sa_flags = SA_SIGINFO | SA_NODEFER | SA_ONSTACK,
+  };
+  HANDLE_ERROR_FROM_ERRNO(sigemptyset(&Action.sa_mask));
+  HANDLE_ERROR_FROM_ERRNO(sigaction(SIGSEGV, &Action, nullptr));
+
+  HANDLE_ERROR_FROM_ERRNO(raise(SIGSEGV));
+
+  return nullptr;
+}
+
+} // namespace
+
+// Check that __asan_handle_no_return properly unpoisons a signal alternate
+// stack.
+// __asan_handle_no_return tries to determine the stack boundaries and
+// unpoisons all memory inside those.
+// If the determination of stack boundaries does not work properly on the
+// signal alternate stack, then the unpoisoning might be omitted.
+// This can leave redzones for variables on the signal alternate stack, which
+// can lead to false positive reports when the stack is reused.
+int main() {
+  size_t const PageSize = sysconf(_SC_PAGESIZE);
+  // To align the alternate stack, we round this up to page_size.
+  size_t const DefaultStackSize =
+      (PTHREAD_STACK_MIN - 1 + PageSize) & ~(PageSize - 1);
+  // The alternate stack needs a certain size, or the signal handler segfaults.
+  size_t const AltStackSize = 10 * PageSize;
+  size_t const MappingSize = DefaultStackSize + AltStackSize;
+  // Using mmap guarantees proper alignment.
+  void *const Mapping = mmap(nullptr, MappingSize,
+                             PROT_READ | PROT_WRITE,
+                             MAP_PRIVATE | MAP_ANONYMOUS,
+                             -1, 0);
+  if ((void *)-1 == Mapping)
+    ERROR("mmap", errno);
+
+  stack_t const AltStack = {
+      .ss_sp = (char *)Mapping + DefaultStackSize,
+      .ss_flags = 0,
+      .ss_size = AltStackSize,
+  };
+
+  pthread_t Thread;
+  pthread_attr_t ThreadAttr;
+  HANDLE_ERROR_FROM_RETVAL(pthread_attr_init(&ThreadAttr));
+  HANDLE_ERROR_FROM_RETVAL(pthread_attr_setstack(&ThreadAttr, Mapping,
+                                                 DefaultStackSize));
+  HANDLE_ERROR_FROM_RETVAL(pthread_create(&Thread, &ThreadAttr, &threadFun,
+                                          (void *)&AltStack));
+
+  if (int const Err = pthread_join(Thread, nullptr))
+    if (EINVAL != Err) // EINVAL means the other thread exited first
+      ERROR("pthread_join", Err);
+
+  HANDLE_ERROR_FROM_RETVAL(munmap(Mapping, MappingSize));
+}