mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-24 06:10:12 +00:00
[sanitizer_common] Add experimental flag to tweak dlopen(<main program>) (#71715)
This introduces an experimental flag 'test_only_replace_dlopen_main_program'. When enabled, this will replace dlopen(main program,...) with dlopen(NULL,...), which is the correct way to get a handle to the main program. This can be useful when ASan is statically linked, since dladdr((void*)pthread_join) or similar will return the path to the main program. Note that dlopen(main program,...) never ends well: - PIE in recent glibc versions (glibc bugzilla 24323), or non-PIE: return an error - PIE in current GRTE and older glibc: attempt to load the main program again, leading to reinitializing ASan and failing to remap the shadow memory. --------- Co-authored-by: Thurston Dang <thurston@google.com>
This commit is contained in:
parent
b34d31d2e1
commit
0be4c6b948
@ -59,6 +59,7 @@ set(SANITIZER_NOLIBC_SOURCES
|
||||
set(SANITIZER_LIBCDEP_SOURCES
|
||||
sanitizer_common_libcdep.cpp
|
||||
sanitizer_allocator_checks.cpp
|
||||
sanitizer_dl.cpp
|
||||
sanitizer_linux_libcdep.cpp
|
||||
sanitizer_mac_libcdep.cpp
|
||||
sanitizer_posix_libcdep.cpp
|
||||
@ -139,6 +140,7 @@ set(SANITIZER_IMPL_HEADERS
|
||||
sanitizer_deadlock_detector_interface.h
|
||||
sanitizer_dense_map.h
|
||||
sanitizer_dense_map_info.h
|
||||
sanitizer_dl.h
|
||||
sanitizer_errno.h
|
||||
sanitizer_errno_codes.h
|
||||
sanitizer_file.h
|
||||
|
@ -33,16 +33,17 @@
|
||||
// COMMON_INTERCEPTOR_STRERROR
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "interception/interception.h"
|
||||
#include "sanitizer_addrhashmap.h"
|
||||
#include "sanitizer_dl.h"
|
||||
#include "sanitizer_errno.h"
|
||||
#include "sanitizer_placement_new.h"
|
||||
#include "sanitizer_platform_interceptors.h"
|
||||
#include "sanitizer_symbolizer.h"
|
||||
#include "sanitizer_tls_get_addr.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#if SANITIZER_INTERCEPTOR_HOOKS
|
||||
#define CALL_WEAK_INTERCEPTOR_HOOK(f, ...) f(__VA_ARGS__);
|
||||
#define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...) \
|
||||
@ -6307,7 +6308,36 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
|
||||
INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
|
||||
void *ctx;
|
||||
COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag);
|
||||
if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
|
||||
|
||||
if (filename) {
|
||||
COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
|
||||
|
||||
# if !SANITIZER_DYNAMIC
|
||||
// We care about a very specific use-case: dladdr on
|
||||
// statically-linked ASan may return <main program>
|
||||
// instead of the library.
|
||||
// We therefore only take effect if the sanitizer is statically
|
||||
// linked, and we don't bother canonicalizing paths because
|
||||
// dladdr should return the same address both times (we assume
|
||||
// the user did not canonicalize the result from dladdr).
|
||||
if (common_flags()->test_only_replace_dlopen_main_program) {
|
||||
VPrintf(1, "dlopen interceptor: filename: %s\n", filename);
|
||||
|
||||
const char *SelfFName = DladdrSelfFName();
|
||||
VPrintf(1, "dlopen interceptor: DladdrSelfFName: %p %s\n",
|
||||
(void *)SelfFName, SelfFName);
|
||||
|
||||
if (internal_strcmp(SelfFName, filename) == 0) {
|
||||
// It's possible they copied the string from dladdr, so
|
||||
// we do a string comparison rather than pointer comparison.
|
||||
VPrintf(1, "dlopen interceptor: replacing %s because it matches %s\n",
|
||||
filename, SelfFName);
|
||||
filename = (char *)0; // RTLD_DEFAULT
|
||||
}
|
||||
}
|
||||
# endif // !SANITIZER_DYNAMIC
|
||||
}
|
||||
|
||||
void *res = COMMON_INTERCEPTOR_DLOPEN(filename, flag);
|
||||
Symbolizer::GetOrInit()->InvalidateModuleList();
|
||||
COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res);
|
||||
|
35
compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp
Normal file
35
compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
//===-- sanitizer_dl.cpp --------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file has helper functions that depend on libc's dynamic loading
|
||||
// introspection.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "sanitizer_dl.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
|
||||
namespace __sanitizer {
|
||||
extern const char *SanitizerToolName;
|
||||
|
||||
const char *DladdrSelfFName(void) {
|
||||
#if SANITIZER_GLIBC
|
||||
Dl_info info;
|
||||
int ret = dladdr((void *)&SanitizerToolName, &info);
|
||||
if (ret) {
|
||||
return info.dli_fname;
|
||||
}
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
26
compiler-rt/lib/sanitizer_common/sanitizer_dl.h
Normal file
26
compiler-rt/lib/sanitizer_common/sanitizer_dl.h
Normal file
@ -0,0 +1,26 @@
|
||||
//===-- sanitizer_dl.h ----------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file has helper functions that depend on libc's dynamic loading
|
||||
// introspection.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef SANITIZER_DL_H
|
||||
#define SANITIZER_DL_H
|
||||
|
||||
namespace __sanitizer {
|
||||
|
||||
// Returns the path to the shared object or - in the case of statically linked
|
||||
// sanitizers
|
||||
// - the main program itself, that contains the sanitizer.
|
||||
const char* DladdrSelfFName(void);
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_DL_H
|
@ -269,3 +269,9 @@ COMMON_FLAG(bool, detect_write_exec, false,
|
||||
COMMON_FLAG(bool, test_only_emulate_no_memorymap, false,
|
||||
"TEST ONLY fail to read memory mappings to emulate sanitized "
|
||||
"\"init\"")
|
||||
// With static linking, dladdr((void*)pthread_join) or similar will return the
|
||||
// path to the main program. This flag will replace dlopen(<main program,...>
|
||||
// with dlopen(NULL,...), which is the correct way to get a handle to the main
|
||||
// program.
|
||||
COMMON_FLAG(bool, test_only_replace_dlopen_main_program, false,
|
||||
"TEST ONLY replace dlopen(<main program>,...) with dlopen(NULL)")
|
||||
|
@ -0,0 +1,48 @@
|
||||
// Test 'test_only_replace_dlopen_main_program' flag
|
||||
|
||||
// RUN: %clangxx %s -pie -fPIE -o %t
|
||||
// RUN: env %tool_options='test_only_replace_dlopen_main_program=true' %run %t
|
||||
// RUN: env %tool_options='test_only_replace_dlopen_main_program=false' not %run %t
|
||||
|
||||
// dladdr is 'nonstandard GNU extensions that are also present on Solaris'
|
||||
// REQUIRES: glibc
|
||||
|
||||
// Does not intercept dlopen
|
||||
// UNSUPPORTED: hwasan, lsan, ubsan
|
||||
|
||||
// Flag has no effect with dynamic runtime
|
||||
// UNSUPPORTED: asan-dynamic-runtime
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// We can't use the address of 'main' (error: ISO C++ does not allow 'main' to be used by a program [-Werror,-Wmain]')
|
||||
// so we add this function.
|
||||
__attribute__((noinline, no_sanitize("address"))) void foo() {
|
||||
printf("Hello World!\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
foo();
|
||||
|
||||
// "If filename is NULL, then the returned handle is for the main program."
|
||||
void *correct_handle = dlopen(NULL, RTLD_LAZY);
|
||||
printf("dlopen(NULL,...): %p\n", correct_handle);
|
||||
|
||||
Dl_info info;
|
||||
if (dladdr((void *)&foo, &info) == 0) {
|
||||
printf("dladdr failed\n");
|
||||
return 1;
|
||||
}
|
||||
printf("dladdr(&foo): %s\n", info.dli_fname);
|
||||
void *test_handle = dlopen(info.dli_fname, RTLD_LAZY);
|
||||
printf("dlopen(%s,...): %p\n", info.dli_fname, test_handle);
|
||||
|
||||
if (test_handle != correct_handle) {
|
||||
printf("Error: handles do not match\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -54,6 +54,8 @@ source_set("sources") {
|
||||
"sanitizer_deadlock_detector_interface.h",
|
||||
"sanitizer_dense_map.h",
|
||||
"sanitizer_dense_map_info.h",
|
||||
"sanitizer_dl.cpp",
|
||||
"sanitizer_dl.h",
|
||||
"sanitizer_errno.cpp",
|
||||
"sanitizer_errno.h",
|
||||
"sanitizer_errno_codes.h",
|
||||
|
Loading…
Reference in New Issue
Block a user