This is an archive of the discontinued LLVM Phabricator instance.

Allow initialization of Asan interceptors before the general Asan initialization takes place on FreeBSD
ClosedPublic

Authored by kutuzov.viktor.84 on Jul 14 2014, 7:55 AM.

Diff Detail

Event Timeline

kutuzov.viktor.84 retitled this revision from to Allow initialization of Asan interceptors before the general Asan initialization takes place on FreeBSD.
kutuzov.viktor.84 updated this object.
kutuzov.viktor.84 edited the test plan for this revision. (Show Details)
kutuzov.viktor.84 added reviewers: kcc, samsonov.
kutuzov.viktor.84 added subscribers: Unknown Object (MLST), emaste.

Here's how things currently look during initialization on FreeBSD:

#0  AsanInitInternal () at /usr/home/ik/llvm/llvm.current/projects/compiler-rt/lib/asan/asan_rtl.cc:695
#1  0x0000000000411d7a in __interceptor_strncmp () at sanitizer_common_interceptors.inc:178
#2  0x000000080109e35e in getenv () from /lib/libc.so.7
#3  0x0000000801302d03 in pthread_timedjoin_np () from /lib/libthr.so.3
#4  0x00000008013055d2 in __error () from /lib/libthr.so.3
#5  0x00000008012f9bd6 in .init () from /lib/libthr.so.3
#6  0x00007fffffffd0d0 in ?? ()
#7  0x000000080065d611 in r_debug_state () from /libexec/ld-elf.so.1
#8  0x000000080065cc97 in __tls_get_addr () from /libexec/ld-elf.so.1
#9  0x000000080065b089 in .text () from /libexec/ld-elf.so.1
#10 0x0000000000000000 in ?? ()

Note that an intercepted function is called before Asan has a chance to initialize itself.

The fix eliminates mass failures on Asan tests--both unit and lit ones.

kcc edited edge metadata.Jul 14 2014, 11:45 PM

This overcomplicates things that are already overcomplicated to my taste.
I especially dislike extra if(FREEBSD).

What is wrong with the stack you've shown?

kutuzov.viktor.84 added a comment.EditedJul 15 2014, 1:56 AM

What is wrong with the stack you've shown?

The problem is that strncmp() is called before REAL(strncmp) is initialized so we have to do InitializeAsanInterceptors() before we can refer to the real function. Calling AsanInitInternal() cannot proceed correctly as it actually called from within initialization of libthr while AsanInitInternal() itself tries to initialize the thread subsystem what lead us to recursive initialization of libthr.

Can you use ENSURE_ASAN_INITED?

Can you use ENSURE_ASAN_INITED?

It would cause calling AsanInitInternal() which, in turn, will attempt to (recursively) initialize threads.

OK.
So, InitializeAsanInterceptors() here is OK, and AsanInit() is not. Could this be solved by initializing interceptors earlier in AsanInit? This initialization sequence is kind of fragile, and I don't like that we arbitrarily move part of it upwards in a platform-dependent way.

Could this be solved by initializing interceptors earlier in AsanInit? This initialization sequence is kind of fragile, and I don't like that we arbitrarily move part of it upwards in a platform-dependent way.

By the time when the first of the intercepted functions is called, the libthr (the library Asan's thread rely on on FreeBSD) is being initialized already. AsanInitInternal() initializes both the interceptors and threads unconditionally and thus causes recursive initialization of libthr.

The overall process looks like this:

  • the loader calls libthr's .init();
  • .init() calls getenv() during initialization of the library;
  • genenv() relies on strncmp();
  • strncmp() is intercepted, but at this phase the REAL(strncmp) is not yet initialized;
  • since Asan is not initialized, AsanInitInternal() takes place;
  • AsanInitInternal() initializes the references to the real functions and then call AsanTSDInit();
  • AsanTSDInit() calls pthread_key_create();
  • pthread_key_create() sees that libthr is not yet initialized (what means its initialization is not yet completed) and launches initialization of libthr.

So the answer to your question is: unfortunately, it doesn't matter how early interceptors get initialized in AsanInitInternal(); the point is that on FreeBSD the initialization of interceptors and initialization of threads are separate phases that cannot be tied together.

kcc added a comment.Jul 16 2014, 3:16 AM

nice loop.
Maybe we could break the loop by calling internal_strncmp?

INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {

if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
   return internal_strncmp(s1, s2, size);

we have a couple of similar hacks elsewhere

kutuzov.viktor.84 edited edge metadata.

Yes, it worked. Thanks, Kostya. Updated.

kcc accepted this revision.Jul 25 2014, 4:52 AM
kcc edited edge metadata.

LGTM with one small nit

lib/sanitizer_common/sanitizer_common_interceptors.inc
95 ↗(On Diff #11575)

why new line?

This revision is now accepted and ready to land.Jul 25 2014, 4:52 AM
kutuzov.viktor.84 updated this revision to Diff 11874.

Closed by commit rL213941 (authored by vkutuzov).