Index: compiler-rt/include/sanitizer/common_interface_defs.h
===================================================================
--- compiler-rt/include/sanitizer/common_interface_defs.h
+++ compiler-rt/include/sanitizer/common_interface_defs.h
@@ -341,6 +341,30 @@
const void **bottom_old,
size_t *size_old);
+/// Calls the user-provided callback for each Fake stack region for the
+/// specified thread ID.
+///
+/// \param os_tid Thread ID. Only the Fake stack of this thread ID is considered.
+/// \param callback User-provided callback. For each stack region,
+/// callback is called with begin and
+/// end marking the stack span and arg
+/// equal to the arg parameter value passed.
+/// \param arg This value is passed as last parameter to callback.
+void __sanitizer_for_each_extra_stack_range(
+ uint64_t os_tid, void (*callback)(size_t begin, size_t end, void *arg),
+ void *arg);
+
+/// Calls the user-provided callback for each Fake stack region for
+/// threads that have a Fake stack.
+///
+/// \param callback User-provided callback. For each stack region,
+/// callback is called with begin and
+/// end marking the stack span and arg
+/// equal to the user-provided arg value.
+/// \param arg This value is passed as last parameter to callback.
+void __sanitizer_for_each_extra_stack_range_all_threads(
+ void (*callback)(size_t begin, size_t end, void *arg), void *arg);
+
// Get full module name and calculate pc offset within it.
// Returns 1 if pc belongs to some module, 0 if module was not found.
int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_path,
Index: compiler-rt/lib/asan/asan_interface.inc
===================================================================
--- compiler-rt/lib/asan/asan_interface.inc
+++ compiler-rt/lib/asan/asan_interface.inc
@@ -154,6 +154,8 @@
INTERFACE_FUNCTION(__asan_unregister_image_globals)
INTERFACE_FUNCTION(__asan_version_mismatch_check_v8)
INTERFACE_FUNCTION(__sanitizer_finish_switch_fiber)
+INTERFACE_FUNCTION(__sanitizer_for_each_extra_stack_range)
+INTERFACE_FUNCTION(__sanitizer_for_each_extra_stack_range_all_threads)
INTERFACE_FUNCTION(__sanitizer_print_stack_trace)
INTERFACE_FUNCTION(__sanitizer_ptr_cmp)
INTERFACE_FUNCTION(__sanitizer_ptr_sub)
Index: compiler-rt/lib/asan/asan_thread.cpp
===================================================================
--- compiler-rt/lib/asan/asan_thread.cpp
+++ compiler-rt/lib/asan/asan_thread.cpp
@@ -533,4 +533,40 @@
(uptr*)bottom_old,
(uptr*)size_old);
}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_for_each_extra_stack_range(
+ u64 os_id, void (*callback)(uptr begin, uptr end, void *arg), void *arg) {
+ AsanThread *t = GetAsanThreadByOsIDLocked(os_id);
+ if (t && t->has_fake_stack())
+ t->fake_stack()->ForEachFakeFrame(callback, arg);
+}
+
+struct RichRangeIteratorCallback {
+ RangeIteratorCallback callback;
+ void *arg;
+};
+
+static void
+CallRichRangeCallback(ThreadContextBase *tctx_base, void *arg) {
+ auto *cb = reinterpret_cast(arg);
+ auto *tctx = static_cast(tctx_base);
+ AsanThread *t = tctx->thread;
+ if (t && t->has_fake_stack())
+ t->fake_stack()->ForEachFakeFrame(cb->callback, cb->arg);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_for_each_extra_stack_range_all_threads(
+ void (*callback)(uptr begin, uptr end, void *arg), void *arg) {
+ if (!__asan_option_detect_stack_use_after_return)
+ return;
+
+ RichRangeIteratorCallback cb = {callback, arg};
+ {
+ ThreadRegistryLock l(&asanThreadRegistry());
+ asanThreadRegistry().RunCallbackForEachThreadLocked(CallRichRangeCallback,
+ &cb);
+ }
}
+} // extern "C"
Index: compiler-rt/test/asan/TestCases/iterate-fakestacks.cpp
===================================================================
--- /dev/null
+++ compiler-rt/test/asan/TestCases/iterate-fakestacks.cpp
@@ -0,0 +1,55 @@
+// RUN: %clangxx_asan -O0 %s -o %t
+// RUN: %env_asan_opts=detect_stack_use_after_return=1 %run %t 2>&1 | FileCheck %s
+
+#include
+
+#include
+
+// The FakeStack region also contains the redzones on both sides of the local variable(s).
+// This means that reading from the region will read from the redzones too, which would
+// trigger ASan stack-buffer-overread errors. Disabling instrumentation of this function
+// bypasses the poison check / solves the errors. The purpose of this testcase is not to test
+// whether ASan triggers on bad reads, but instead to test whether fakestack iteration
+// callback is called with the correct address range that includes local variables.
+__attribute__((no_sanitize("address")))
+void callback(size_t begin, size_t end, void *arg) {
+ printf("%s:", arg);
+ for (auto p = begin; p < end; p++) {
+ char *c = (char*)p;
+ if (*c >= 32) // Filter out ASCII control codes
+ printf("%c", *c);
+ }
+ printf("\n");
+}
+
+__attribute__((noinline)) void frame3() {
+ char localvars[6] = "Three";
+ const char *arg = "from frame three";
+ printf("FRAME3\n");
+ __sanitizer_for_each_extra_stack_range_all_threads(&callback, (void*)arg);
+}
+
+__attribute__((noinline)) void frame2() {
+ char localvars[50] = "A$@N";
+ frame3();
+}
+
+int main(int argc, char **argv) {
+ char localvars[8] = "L1V|\\/|";
+ frame2();
+
+ const char *arg = "from main";
+ printf("MAIN\n");
+ __sanitizer_for_each_extra_stack_range_all_threads(&callback, (void*)arg);
+ printf("DONE\n");
+ return 0;
+}
+
+// CHECK: FRAME3
+// There is no guarantee on the iteration order of the frames
+// CHECK-DAG: from frame three:{{.*}}Three
+// CHECK-DAG: from frame three:{{.*}}A$@N
+// CHECK-DAG: from frame three:{{.*}}L1V|\/|
+// CHECK: MAIN
+// CHECK-NEXT: from main:{{.*}}L1V|\/|
+// CHECK-NEXT: DONE