LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 39641 - asan hangs at exception
Summary: asan hangs at exception
Status: RESOLVED FIXED
Alias: None
Product: compiler-rt
Classification: Unclassified
Component: asan (show other bugs)
Version: 8.0
Hardware: PC Linux
: P enhancement
Assignee: sguelton
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2018-11-12 14:26 PST by stsp
Modified: 2019-07-18 01:14 PDT (History)
4 users (show)

See Also:
Fixed By Commit(s):


Attachments
test case (564 bytes, application/gzip)
2018-11-12 14:26 PST, stsp
Details

Note You need to log in before you can comment on or make changes to this bug.
Description stsp 2018-11-12 14:26:11 PST
Created attachment 21114 [details]
test case

Hello.

Attached is a test-case for the problem.
If some C++ shlib is dlopen()ed by the C program,
and then this shlib throws an exception (that it
also catches of course - invisible to the C code),
then asan fails to find __cxa_throw, and calls the
__asan_handle_no_return() in an infinite loop.

Another bug here is that it is not possible to
see the symbols of asan functions:
---
(gdb) bt
#0  0x00000000004e6bf9 in __asan::AsanThread::stack_top() ()
#1  0x00000000004e4785 in __asan_handle_no_return ()
#2  0x000000000043304c in __interceptor___cxa_throw ()
#3  0x00007ffff40fea9f in foo () at shlib.cpp:4
#4  0x00007ffff40fe97d in bar () at shlib.cpp:13
#5  0x00000000005121eb in main () at main.c:11
---

... even though everything was compiled with debug info.
Comment 1 stsp 2018-11-15 07:21:26 PST
The reason appears simple.
clang statically links asan to the main
executable, and not to shared lib.
As the result, we get this:
---
Run till exit from #0  __dlsym (handle=0xffffffffffffffff, 
    name=0x5195e8 "__cxa_throw") at dlsym.c:56
0x00000000004e8e85 in __interception::GetRealFunctionAddress(char const*, unsigned long*, unsigned long, unsigned long) ()
Value returned is $1 = (void *) 0x0
(gdb) bt
#0  0x00000000004e8e85 in __interception::GetRealFunctionAddress(char const*, unsigned long*, unsigned long, unsigned long) ()
#1  0x00000000004d2db3 in __asan::InitializeAsanInterceptors() ()
#2  0x00000000004e4c84 in __asan::AsanInitInternal() [clone .part.0] ()
#3  0x00007ffff7de5806 in _dl_init (main_map=0x7ffff7ffe170, argc=1, 
    argv=0x7fffffffdf08, env=0x7fffffffdf18) at dl-init.c:104
#4  0x00007ffff7dd60ca in _dl_start_user () from /lib64/ld-linux-x86-64.so.2
---

Since the main executable is not linked to
libstdc++ (only the C++ shlib is linked to it),
dlsym() can't resolve __cxa_throw.
The additional bug, compared to gcc's asan, is
that clang's one doesn't check the dlsym() failure
and that leads to hangs (not sure why a hang and
not a NULL deref).
In gcc things are much better: it uses dynamic
linking of libasan (not static, as clang), and
it checks the dlsym() return, so with gcc there
is only a run-time error msg.
Comment 2 sguelton 2019-06-18 06:28:17 PDT
This is still reproducible with compiler-rt master version.
The infinite loop is caused by asan choosing to dlsym on its interceptor when finding next symbol fails (in your case because libstdc++ is not loaded yet).

A nice way to fix that error would be to lazily load interceptors, but that would come at a small runtime cost.

Adding Kostya Serebryany for more advices...
Comment 3 Kostya Serebryany 2019-06-18 16:26:18 PDT
Is there an easy workaround in the build command line(s) for this program? 
I am reluctant to support this case if it adds further complexity to asan.
Comment 4 sguelton 2019-06-18 23:05:02 PDT
The workaround is trivial: pass `-lstdc++` to the linker when compiling the main function.

That way, when asan builds its interceptors, the `__cxa_throw`` symbol is found by dlopen.
Comment 5 stsp 2019-06-19 17:28:25 PDT
Is this somehow specific to libstdc++,
or this "workaround" will have to be applied
for all deps, recursively?

Also, gcc's asan at least doesn't hang in an
infinite loop. It prints an error and goes on,
which is a big win by itself.
Comment 6 sguelton 2019-06-20 02:22:30 PDT
It's not specific to libstdc++, the issue would raise for any shared library linked into a dlopened library and not in the main executable.

I agree with the error handling aspect, @kostya, any idea to improve the situation?
Comment 7 Kostya Serebryany 2019-06-21 10:34:22 PDT
No good ideas, sorry, except that patches are welcome
(as long as they don't add too much complexity)
Comment 8 sguelton 2019-06-26 00:54:45 PDT
@Kostya:

in this snippet

```
INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
  CHECK(REAL(__cxa_throw));
  __asan_handle_no_return();
  REAL(__cxa_throw)(a, b, c);
}
```

we could check whether REAL(__cxa_throw) != __cxa_throw, which is the cause of the origin loop. Even better, if REAL(__cxa_throw) == __cxa_throw, we could try a new dlsym lookup because if the input program is correct, then __cxa_throw must be available when called.

Taking this one step further, there is no need to run the dlsym at startup, it could be done lazily upon first call :-)
Comment 9 Kostya Serebryany 2019-06-26 10:19:40 PDT
I'd like to avoid any kind of lazy init, it usually adds complexity more than it removes it. 
If you can come up with a simple check in the interceptor, and a test, 
please submit a patch.
Comment 10 sguelton 2019-06-27 08:18:16 PDT
https://reviews.llvm.org/D63877 now raises a decent error
Comment 11 sguelton 2019-07-18 01:14:38 PDT
Fixed by https://reviews.llvm.org/rL366413