Skip to content

Commit

Permalink
Introduce a way to allow the ASan dylib on Darwin platforms to be loa…
Browse files Browse the repository at this point in the history
…ded via `dlopen()`.

Summary:

The purpose of this option is provide a way for the ASan dylib
to be loaded via `dlopen()` without triggering most initialization
steps (e.g. shadow memory set up) that normally occur when the
ASan dylib is loaded.

This new functionality is exposed by

- A `SANITIZER_SUPPORTS_INIT_FOR_DLOPEN` macro which indicates if the
  feature is supported. This only true for Darwin currently.
- A `HandleDlopenInit()` function which should return true if the library
  is being loaded via `dlopen()` and
  `SANITIZER_SUPPORTS_INIT_FOR_DLOPEN` is supported. Platforms that
  support this may perform any initialization they wish inside this
  function.

Although disabling initialization is something that could potentially
apply to other sanitizers it appears to be unnecessary for other
sanitizers so this patch only makes the change for ASan.

rdar://problem/45284065

Reviewers: kubamracek, george.karpenkov, kcc, eugenis, krytarowski

Subscribers: #sanitizers, llvm-commits

Differential Revision: https://reviews.llvm.org/D54469

llvm-svn: 348078
  • Loading branch information
danliew committed Dec 1, 2018
1 parent 102854f commit 8bffb63
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 1 deletion.
7 changes: 7 additions & 0 deletions compiler-rt/lib/asan/asan_fuchsia.cc
Expand Up @@ -190,6 +190,13 @@ static void ThreadExitHook(void *hook, uptr os_id) {
AsanThread::TSDDtor(per_thread);
}

bool HandleDlopenInit() {
// Not supported on this platform.
static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
"Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
return false;
}

} // namespace __asan

// These are declared (in extern "C") by <zircon/sanitizer.h>.
Expand Down
5 changes: 5 additions & 0 deletions compiler-rt/lib/asan/asan_internal.h
Expand Up @@ -111,6 +111,11 @@ void *AsanDlSymNext(const char *sym);

void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name);

// Returns `true` iff most of ASan init process should be skipped due to the
// ASan library being loaded via `dlopen()`. Platforms may perform any
// `dlopen()` specific initialization inside this function.
bool HandleDlopenInit();

// Add convenient macro for interface functions that may be represented as
// weak hooks.
#define ASAN_MALLOC_HOOK(ptr, size) \
Expand Down
7 changes: 7 additions & 0 deletions compiler-rt/lib/asan/asan_linux.cc
Expand Up @@ -248,6 +248,13 @@ void *AsanDlSymNext(const char *sym) {
return dlsym(RTLD_NEXT, sym);
}

bool HandleDlopenInit() {
// Not supported on this platform.
static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
"Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
return false;
}

} // namespace __asan

#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
Expand Down
21 changes: 21 additions & 0 deletions compiler-rt/lib/asan/asan_malloc_mac.cc
Expand Up @@ -61,4 +61,25 @@ using namespace __asan;

#include "sanitizer_common/sanitizer_malloc_mac.inc"

namespace COMMON_MALLOC_NAMESPACE {
bool HandleDlopenInit() {
static_assert(SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
"Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be true");
// We have no reliable way of knowing how we are being loaded
// so make it a requirement on Apple platforms to set this environment
// variable to indicate that we want to perform initialization via
// dlopen().
auto init_str = GetEnv("APPLE_ASAN_INIT_FOR_DLOPEN");
if (!init_str)
return false;
if (internal_strncmp(init_str, "1", 1) != 0)
return false;
// When we are loaded via `dlopen()` path we still initialize the malloc zone
// so Symbolication clients (e.g. `leaks`) that load the ASan allocator can
// find an initialized malloc zone.
InitMallocZoneFields();
return true;
}
} // namespace COMMON_MALLOC_NAMESPACE

#endif
6 changes: 6 additions & 0 deletions compiler-rt/lib/asan/asan_rtems.cc
Expand Up @@ -213,6 +213,12 @@ static void HandleExit() {
}
}

bool HandleDlopenInit() {
// Not supported on this platform.
static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
"Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
return false;
}
} // namespace __asan

// These are declared (in extern "C") by <some_path/sanitizer.h>.
Expand Down
8 changes: 8 additions & 0 deletions compiler-rt/lib/asan/asan_rtl.cc
Expand Up @@ -396,6 +396,14 @@ static void AsanInitInternal() {
// initialization steps look at flags().
InitializeFlags();

// Stop performing init at this point if we are being loaded via
// dlopen() and the platform supports it.
if (SANITIZER_SUPPORTS_INIT_FOR_DLOPEN && UNLIKELY(HandleDlopenInit())) {
asan_init_is_running = false;
VReport(1, "AddressSanitizer init is being performed for dlopen().\n");
return;
}

AsanCheckIncompatibleRT();
AsanCheckDynamicRTPrereqs();
AvoidCVE_2016_2143();
Expand Down
7 changes: 7 additions & 0 deletions compiler-rt/lib/asan/asan_win.cc
Expand Up @@ -322,6 +322,13 @@ int __asan_set_seh_filter() {
return 0;
}

bool HandleDlopenInit() {
// Not supported on this platform.
static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
"Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
return false;
}

#if !ASAN_DYNAMIC
// The CRT runs initializers in this order:
// - C initializers, from XIA to XIZ
Expand Down
9 changes: 9 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_platform.h
Expand Up @@ -342,4 +342,13 @@
#define SANITIZER_SYMBOLIZER_MARKUP 0
#endif

// Enable ability to support sanitizer initialization that is
// compatible with the sanitizer library being loaded via
// `dlopen()`.
#if SANITIZER_MAC
#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 1
#else
#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 0
#endif

#endif // SANITIZER_PLATFORM_H
46 changes: 46 additions & 0 deletions compiler-rt/test/asan/TestCases/Darwin/init_for_dlopen.cc
@@ -0,0 +1,46 @@
// RUN: %clangxx -g -O0 %s -o %t

// Check that trying to dlopen() the ASan dylib fails.
// We explictly set `abort_on_error=0` because
// - By default the lit config sets this but we don't want this
// test to implicitly depend on this.
// - It avoids requiring `--crash` to be passed to `not`.
// RUN: APPLE_ASAN_INIT_FOR_DLOPEN=0 %env_asan_opts=abort_on_error=0 not \
// RUN: %run %t %shared_libasan 2>&1 | \
// RUN: FileCheck -check-prefix=CHECK-DL-OPEN-FAIL %s
// RUN: env -u APPLE_ASAN_INIT_FOR_DLOPEN %env_asan_opts=abort_on_error=0 not \
// RUN: %run %t %shared_libasan 2>&1 | \
// RUN: FileCheck -check-prefix=CHECK-DL-OPEN-FAIL %s

// Check that we can successfully dlopen the ASan dylib when we set the right
// environment variable.
// RUN: env APPLE_ASAN_INIT_FOR_DLOPEN=1 %run %t %shared_libasan 2>&1 | \
// RUN: FileCheck -check-prefix=CHECK-DL-OPEN-SUCCESS %s

#include <dlfcn.h>
#include <stdio.h>

// CHECK-DL-OPEN-FAIL: ERROR: Interceptors are not working

int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <dylib_path>\n", argv[0]);
return 1;
}
const char *dylib_path = argv[1];
void *handle = dlopen(dylib_path, RTLD_LAZY);
if (!handle) {
fprintf(stderr, "Failed to dlopen: %s\n", dlerror());
return 1;
}
// Make sure we can find a function we expect to be in the dylib.
void *fn = dlsym(handle, "__sanitizer_mz_size");
if (!fn) {
fprintf(stderr, "Failed to get symbol: %s\n", dlerror());
return 1;
}
// TODO(dliew): Actually call a function from the dylib that is safe to call.
// CHECK-DL-OPEN-SUCCESS: DONE
printf("DONE\n");
return 0;
}
Expand Up @@ -8,7 +8,7 @@

device_id = os.environ["SANITIZER_IOSSIM_TEST_DEVICE_IDENTIFIER"]

for e in ["ASAN_OPTIONS", "TSAN_OPTIONS", "UBSAN_OPTIONS"]:
for e in ["ASAN_OPTIONS", "TSAN_OPTIONS", "UBSAN_OPTIONS", "APPLE_ASAN_INIT_FOR_DLOPEN"]:
if e in os.environ:
os.environ["SIMCTL_CHILD_" + e] = os.environ[e]

Expand Down

0 comments on commit 8bffb63

Please sign in to comment.