This is an archive of the discontinued LLVM Phabricator instance.

[asan] Intercept std::rethrow_exception indirectly.
ClosedPublic

Authored by robot on Jan 29 2018, 7:05 AM.

Details

Summary

Fixes Bug 32434
See https://bugs.llvm.org/show_bug.cgi?id=32434

Short summary:
std::rethrow_exception does not use __cxa_throw to rethrow the exception, so if
it is called from uninstrumented code, it will leave the stack poisoned. This
can lead to false positives.

Long description:

For functions which don't return normally (e.g. via exceptions), asan needs to
unpoison the entire stack. It is not known before a call to such a function
where execution will continue, some function which don't contain cleanup code
like destructors might be skipped. After stack unwinding, execution might
continue in uninstrumented code.

If the stack has been poisoned before such a function is called, but the stack
is unwound during the unconventional return, then zombie redzones (entries) for
no longer existing stack variables can remain in the shadow memory. Normally,
this is avoided by asan generating a call to asan_handle_no_return before all
functions marked as [[noreturn]]. This
asan_handle_no_return unpoisons the
entire stack. Since these [[noreturn]] functions can be called from
uninstrumented code, asan also introduces interceptor functions which call
asan_handle_no_return before running the original [[noreturn]] function;
for example,
cxa_throw is intercepted.

If a [[noreturn]] function is called from uninstrumented code (so the stack is
left poisoned) and additionally, execution continues in uninstrumented code, new
stack variables might be introduced and overlap with the stack variables
which have been removed during stack unwinding. Since the redzones are not
cleared nor overwritten by uninstrumented code, they remain but now contain
invalid data.

Now, if the redzones are checked against the new stack variables, false
positive reports can occur. This can happen for example by the uninstrumented
code calling an intercepted function such as memcpy, or an instrumented
function.

Intercepting std::rethrow_exception directly is not easily possible since it
depends on the C++ standard library implementation (e.g. libcxx vs libstdc++)
and the mangled name it produces for this function. As a rather simple
workaround, we're intercepting _Unwind_RaiseException for libstdc++. For
libcxxabi, we can intercept the ABI function __cxa_rethrow_primary_exception.

Diff Detail

Event Timeline

robot created this revision.Jan 29 2018, 7:05 AM
Herald added subscribers: Restricted Project, llvm-commits, hintonda and 2 others. · View Herald TranscriptJan 29 2018, 7:05 AM
robot added a reviewer: kcc.Jan 29 2018, 7:14 AM
cryptoad edited reviewers, added: eugenis, alekseyshl; removed: cryptoad.Jan 29 2018, 8:00 AM

Cool. Can we get a lit test as well?

robot added a comment.Feb 1 2018, 7:05 AM

Cool. Can we get a lit test as well?

This is our first patch so we're unfamiliar with the LLVM testing infrastructure. Could you please tell us what kind of test you'd like? An example would also be great.

We still have some concerns regarding the portability of this patch: we don’t know how to portably intercept a C++ standard library function (name mangling, layout of types, …). In libc++abi, there’s __cxa_rethrow_primary_exception but it is not used by libstdc++. Instead, libstdc++’s implementation calls either _Unwind_RaiseException or _Unwind_SjLj_RaiseException directly – so we’ve intercepted those two for libstdc++. We didn’t check if this helps on other platforms (Mac and Windows come to mind).

Additionally, it might be possible there are some other interceptor functions missing such as __cxa_rethrow (for throw; statements in catch blocks) which might lead to analogous false positives (especially if you don’t intercept _Unwind_RaiseException in the libc++ case).

Arnaud & Robot

This is our first patch so we're unfamiliar with the LLVM testing infrastructure. Could you please tell us what kind of test you'd like? An example would also be great.

Thank you for your first contribution! I'm going to comment on the testing infrastructure only, as I don't know the answer to the portability/mangling/ABI question.

Most user-visible features of ASan can usually be demonstrated in a single-file test that is completely standalone test program. E.g. ASan's ability to detect heap buffer overflows is demonstrated in test/asan/TestCases/heap-overflow.cc file. Notice that these tests don't include or reference any sanitizer internal functions. They are meant to be written in a way that user's code is written. Can you try adding such a test, which would be a standalone .cc file that would demonstrate the problematic scenario (C++ exception rethrow) without including ASan headers? The test's expectation should be that ASan doesn't report any issues, whereas without your patch, the test fails (or might fail). For an example of a negative test (that checks that ASan *doesn't* report any issues), see test/asan/TestCases/Darwin/nil-return-struct.mm.

If the test should work on all ASan supported platforms, put it directly into test/asan/TestCases. If it's POSIX-only, place it under Posix/ subdirectory.

Could you please reformat it?
With git I usually use: git clang-format -f --style=file HEAD^

I would not worry about cross platform here. You can patch just Linux and whoever have access (and issues) on other platforms can send a patch with similar changes.
Mac should use libc++.
I'd put the test outside of Posix, and mark failing platforms as "// XFAIL:", temporarily, as we suppose to handle all of them.

lib/asan/asan_interceptors.cc
329

Should this be: CHECK(REAL(__cxa_rethrow_primary_exception)); ?

robot updated this revision to Diff 132990.Feb 6 2018, 6:47 AM
  • Reformatted using clang-format
  • Fixed copy&waste error CHECK(REAL(__cxa_throw))
  • moved test from asan unit test to lit tests
robot marked an inline comment as done.Feb 22 2018, 7:39 AM

Have you had the time to review the second revision?

vitalybuka accepted this revision.Feb 25 2018, 2:54 PM
vitalybuka added inline comments.
test/asan/TestCases/intercept-rethrow-exception.cc
49

You can include #include <sanitizer/asan_interface.h>
and use __asan_region_is_poisoned

This revision is now accepted and ready to land.Feb 25 2018, 2:54 PM
robot added inline comments.Feb 26 2018, 7:00 AM
test/asan/TestCases/intercept-rethrow-exception.cc
49

We don't have commit rights, can you commit it? Should we first change to __asan_region_is_poisoned and kill the program if it is indeed poisoined?

vitalybuka added inline comments.Feb 26 2018, 10:10 AM
test/asan/TestCases/intercept-rethrow-exception.cc
49

Sure, I'll update and commit this for you.

This revision was automatically updated to reflect the committed changes.

This looks like broke ASan on NetBSD:

$ sh ./projects/compiler-rt/test/sanitizer_common/asan-i386-NetBSD/NetBSD/Output/ttyent.cc.script
/usr/lib/i386/libgcc.a(unwind-dw2.o): In function `_Unwind_RaiseException':
unwind-dw2.c:(.text+0x1b41): multiple definition of `_Unwind_RaiseException'
/public/llvm-build/lib/clang/7.0.0/lib/netbsd/libclang_rt.asan-i386.a(asan_interceptors.cc.o):/public/llvm/projects/compiler-rt/lib/asan/asan_interceptors.cc:337: first defined here
clang-7.0: error: linker command failed with exit code 1 (use -v to see invocation)

Investigating.