diff --git a/compiler-rt/lib/msan/CMakeLists.txt b/compiler-rt/lib/msan/CMakeLists.txt --- a/compiler-rt/lib/msan/CMakeLists.txt +++ b/compiler-rt/lib/msan/CMakeLists.txt @@ -5,6 +5,7 @@ msan.cpp msan_allocator.cpp msan_chained_origin_depot.cpp + msan_dl.cpp msan_interceptors.cpp msan_linux.cpp msan_report.cpp @@ -20,6 +21,7 @@ msan.h msan_allocator.h msan_chained_origin_depot.h + msan_dl.h msan_flags.h msan_flags.inc msan_interface_internal.h diff --git a/compiler-rt/lib/msan/msan_dl.h b/compiler-rt/lib/msan/msan_dl.h new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/msan/msan_dl.h @@ -0,0 +1,51 @@ +#ifndef MSAN_DL_H +#define MSAN_DL_H + +#include "msan.h" +#include "sanitizer_common/sanitizer_common.h" + +namespace __msan { + +void UnpoisonDllAddr1ExtraInfo(void **extra_info, int flags); + +struct dlinfo { + char *dli_fname; + void *dli_fbase; + char *dli_sname; + void *dli_saddr; +}; + +#if SANITIZER_WORDSIZE == 32 +struct sym { + u32 st_name; + u32 st_value; + u32 st_size; + unsigned char st_info; + unsigned char st_other; + u16 st_shndx; +}; +#elif SANITIZER_WORDSIZE == 64 +struct sym { + u32 st_name; + unsigned char st_info; + unsigned char st_other; + u16 st_shndx; + u64 st_value; + u64 st_size; +}; +#else +# error "Unexpected SANITIZER_WORDSIZE" +#endif + +// 'sanitizer_common/sanitizer_linux.h' defines 'struct link_map' +// as an opaque type. +struct link_map_internals { + unsigned long l_addr; + char *l_name; + unsigned long l_ld; + struct link_map_internals *l_next, *l_prev; +}; + +} // namespace __msan + +#endif // MSAN_DL_H diff --git a/compiler-rt/lib/msan/msan_dl.cpp b/compiler-rt/lib/msan/msan_dl.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/lib/msan/msan_dl.cpp @@ -0,0 +1,39 @@ +#include "msan_dl.h" + +#include "msan_poisoning.h" + +namespace __msan { + +void UnpoisonDllAddr1ExtraInfo(void **extra_info, int flags) { + if (flags == 1) { // RTLD_DL_SYMENT + __msan_unpoison(extra_info, sizeof(void *)); + + struct sym *s = *((struct sym **)(extra_info)); + __msan_unpoison(s, sizeof(struct sym)); + } else if (flags == 2) { // RTLD_DL_LINKMAP + __msan_unpoison(extra_info, sizeof(void *)); + + struct link_map_internals *map = + *((struct link_map_internals **)(extra_info)); + + // Walk forward + struct link_map_internals *ptr = map; + while (ptr) { + __msan_unpoison(ptr, sizeof(struct link_map_internals)); + if (ptr->l_name) + __msan_unpoison(ptr->l_name, internal_strlen(ptr->l_name) + 1); + ptr = ptr->l_next; + } + + // Walk backward + ptr = map->l_prev; + while (ptr) { + __msan_unpoison(ptr, sizeof(struct link_map_internals)); + if (ptr->l_name) + __msan_unpoison(ptr->l_name, internal_strlen(ptr->l_name) + 1); + ptr = ptr->l_prev; + } + } +} + +} // namespace __msan diff --git a/compiler-rt/lib/msan/msan_interceptors.cpp b/compiler-rt/lib/msan/msan_interceptors.cpp --- a/compiler-rt/lib/msan/msan_interceptors.cpp +++ b/compiler-rt/lib/msan/msan_interceptors.cpp @@ -17,6 +17,7 @@ #include "interception/interception.h" #include "msan.h" #include "msan_chained_origin_depot.h" +#include "msan_dl.h" #include "msan_origin.h" #include "msan_poisoning.h" #include "msan_report.h" @@ -1518,13 +1519,6 @@ return res; } -struct dlinfo { - char *dli_fname; - void *dli_fbase; - char *dli_sname; - void *dli_saddr; -}; - INTERCEPTOR(int, dladdr, void *addr, dlinfo *info) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, dladdr, addr, info); @@ -1539,6 +1533,23 @@ return res; } +INTERCEPTOR(int, dladdr1, void *addr, dlinfo *info, void **extra_info, + int flags) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, dladdr1, addr, info, extra_info, flags); + int res = REAL(dladdr1)(addr, info, extra_info, flags); + if (res != 0) { + __msan_unpoison(info, sizeof(*info)); + if (info->dli_fname) + __msan_unpoison(info->dli_fname, internal_strlen(info->dli_fname) + 1); + if (info->dli_sname) + __msan_unpoison(info->dli_sname, internal_strlen(info->dli_sname) + 1); + + UnpoisonDllAddr1ExtraInfo(extra_info, flags); + } + return res; +} + INTERCEPTOR(char *, dlerror, int fake) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, dlerror, fake); @@ -1786,6 +1797,7 @@ MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT; INTERCEPT_FUNCTION(strsignal); INTERCEPT_FUNCTION(dladdr); + INTERCEPT_FUNCTION(dladdr1); INTERCEPT_FUNCTION(dlerror); INTERCEPT_FUNCTION(dl_iterate_phdr); INTERCEPT_FUNCTION(getrusage); diff --git a/compiler-rt/test/msan/dladdr1_test.c b/compiler-rt/test/msan/dladdr1_test.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/msan/dladdr1_test.c @@ -0,0 +1,95 @@ +/* RUN: %clang_msan -g %s -o %t + RUN: %clang_msan -g %s -DBUILD_SO -fPIC -o %t-so.so -shared + RUN: %run %t 2>&1 | FileCheck %s + + REQUIRES: glibc{{.*}} +*/ + +#define _GNU_SOURCE + +#ifndef BUILD_SO +#include +#include +#include +#include +#include +#include + +typedef volatile long *(* get_t)(); +get_t GetTls; + +int main(int argc, char *argv[]) { + char path[4096]; + snprintf(path, sizeof(path), "%s-so.so", argv[0]); + int i; + + void *handle = dlopen(path, RTLD_LAZY); + if (!handle) fprintf(stderr, "%s\n", dlerror()); + assert(handle != 0); + GetTls = (get_t)dlsym(handle, "GetTls"); + assert(dlerror() == 0); + + { + printf("Testing RTLD_DL_LINKMAP\n"); + fflush(stdout); + + Dl_info info; + struct link_map *map_ptr; + int ret = dladdr1(GetTls, &info, (void**)(&map_ptr), RTLD_DL_LINKMAP); + assert(ret != 0); + printf("fname: %s\n", info.dli_fname); + printf("fbase: %p\n", info.dli_fbase); + printf("sname: %s\n", info.dli_sname); + // CHECK: sname: GetTls + printf("saddr: %p\n", info.dli_saddr); + + assert(map_ptr != NULL); + printf("map_ptr: %p\n", map_ptr); + fflush(stdout); + + // Find the start of the link map + while(map_ptr->l_prev != NULL) { + fflush(stdout); + map_ptr = map_ptr->l_prev; + } + + fflush(stdout); + while(map_ptr != NULL) { + assert(map_ptr->l_name != NULL); + printf("0x%lx: '%s', %p\n", map_ptr->l_addr, map_ptr->l_name, map_ptr->l_ld); + fflush(stdout); + map_ptr = map_ptr->l_next; + } + // CHECK: libc.so + // CHECK: dladdr1_test + } + + // Test RTLD_DL_SYMENT + + { + printf("Testing RTLD_DL_SYMENT\n"); + fflush(stdout); + + Dl_info info; + ElfW(Sym) *sym; + int ret = dladdr1(GetTls, &info, (void**)(&sym), RTLD_DL_SYMENT); + assert(ret != 0); + printf("fname: %s\n", info.dli_fname); + printf("fbase: %p\n", info.dli_fbase); + printf("sname: %s\n", info.dli_sname); + // CHECK: sname: GetTls + printf("saddr: %p\n", info.dli_saddr); + + printf("sym: %d %d %d %d %lu %lu\n", + sym->st_name, sym->st_info, sym->st_other, + sym->st_shndx, sym->st_value, sym->st_size); + // CHECK: sym: + } + return 0; +} +#else // BUILD_SO +long var; +long *GetTls() { + return &var; +} +#endif