| 1 | // Regression test for |
| 2 | // https://bugs.llvm.org/show_bug.cgi?id=32434 |
| 3 | |
| 4 | // REQUIRES: shared_cxxabi |
| 5 | |
| 6 | // RUN: %clangxx_asan -fexceptions -O0 %s -o %t |
| 7 | // RUN: %env_asan_opts=detect_stack_use_after_return=0 %run %t |
| 8 | |
| 9 | // The current implementation of this functionality requires special |
| 10 | // combination of libraries that are not used by default on NetBSD |
| 11 | // XFAIL: target={{.*netbsd.*}} |
| 12 | // FIXME: Bug 42703 |
| 13 | // XFAIL: target={{.*solaris.*}} |
| 14 | |
| 15 | // https://reviews.llvm.org/D111703 made compiler incompatible with released NDK. |
| 16 | // UNSUPPORTED: android && arm-target-arch |
| 17 | |
| 18 | #include "defines.h" |
| 19 | #include <assert.h> |
| 20 | #include <exception> |
| 21 | #include <sanitizer/asan_interface.h> |
| 22 | |
| 23 | namespace { |
| 24 | |
| 25 | // Not instrumented because std::rethrow_exception is a [[noreturn]] function, |
| 26 | // for which the compiler would emit a call to __asan_handle_no_return which |
| 27 | // unpoisons the stack. |
| 28 | // We emulate here some code not compiled with asan. This function is not |
| 29 | // [[noreturn]] because the scenario we're emulating doesn't always throw. If it |
| 30 | // were [[noreturn]], the calling code would emit a call to |
| 31 | // __asan_handle_no_return. |
| 32 | void ATTRIBUTE_NO_SANITIZE_ADDRESS |
| 33 | uninstrumented_rethrow_exception(std::exception_ptr const &exc_ptr) { |
| 34 | std::rethrow_exception(exc_ptr); |
| 35 | } |
| 36 | |
| 37 | char *poisoned1; |
| 38 | char *poisoned2; |
| 39 | |
| 40 | // Create redzones for stack variables in shadow memory and call |
| 41 | // std::rethrow_exception which should unpoison the entire stack. |
| 42 | void create_redzones_and_throw(std::exception_ptr const &exc_ptr) { |
| 43 | char a[100]; |
| 44 | poisoned1 = a - 1; |
| 45 | poisoned2 = a + sizeof(a); |
| 46 | assert(__asan_address_is_poisoned(poisoned1)); |
| 47 | assert(__asan_address_is_poisoned(poisoned2)); |
| 48 | uninstrumented_rethrow_exception(exc_ptr); |
| 49 | } |
| 50 | |
| 51 | } // namespace |
| 52 | |
| 53 | // Check that std::rethrow_exception is intercepted by asan and the interception |
| 54 | // unpoisons the stack. |
| 55 | // If std::rethrow_exception is NOT intercepted, then calls to this function |
| 56 | // from instrumented code will still unpoison the stack because |
| 57 | // std::rethrow_exception is a [[noreturn]] function and any [[noreturn]] |
| 58 | // function call will be instrumented with __asan_handle_no_return. |
| 59 | // However, calls to std::rethrow_exception from UNinstrumented code will not |
| 60 | // unpoison the stack, so we need to intercept std::rethrow_exception to |
| 61 | // unpoison the stack. |
| 62 | int main() { |
| 63 | // In some implementations of std::make_exception_ptr, e.g. libstdc++ prior to |
| 64 | // gcc 7, this function calls __cxa_throw. The __cxa_throw is intercepted by |
| 65 | // asan to unpoison the entire stack; since this test essentially tests that |
| 66 | // the stack is unpoisoned by a call to std::rethrow_exception, we need to |
| 67 | // generate the exception_ptr BEFORE we have the local variables poison the |
| 68 | // stack. |
| 69 | std::exception_ptr my_exception_ptr = std::make_exception_ptr("up" ); |
| 70 | |
| 71 | try { |
| 72 | create_redzones_and_throw(my_exception_ptr); |
| 73 | } catch(char const *) { |
| 74 | assert(!__asan_region_is_poisoned(poisoned1, poisoned2 - poisoned1 + 1)); |
| 75 | } |
| 76 | } |
| 77 | |