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,28 @@ +//===-- msan_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 is a part of MemorySanitizer. +// +// Helper functions for unpoisoning results of dladdr and dladdr1. +//===----------------------------------------------------------------------===// + +#ifndef MSAN_DL_H +#define MSAN_DL_H + +#include "msan.h" +#include "sanitizer_common/sanitizer_common.h" + +namespace __msan { + +void UnpoisonDllAddrInfo(void *info); +// void UnpoisonDllAddrInfo(Dl_info *info); +void UnpoisonDllAddr1ExtraInfo(void **extra_info, int flags); + +} // 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,63 @@ +//===-- msan_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 is a part of MemorySanitizer. +// +// Helper functions for unpoisoning results of dladdr and dladdr1. +//===----------------------------------------------------------------------===// + +#include "msan_dl.h" + +#include +#include +#include + +#include "msan_poisoning.h" + +namespace __msan { + +void UnpoisonDllAddrInfo(void *info) { + Dl_info *ptr = (Dl_info *)(info); + __msan_unpoison(ptr, sizeof(*ptr)); + if (ptr->dli_fname) + __msan_unpoison(ptr->dli_fname, internal_strlen(ptr->dli_fname) + 1); + if (ptr->dli_sname) + __msan_unpoison(ptr->dli_sname, internal_strlen(ptr->dli_sname) + 1); +} + +void UnpoisonDllAddr1ExtraInfo(void **extra_info, int flags) { + if (flags == RTLD_DL_SYMENT) { + __msan_unpoison(extra_info, sizeof(void *)); + + const ElfW(Sym) *s = (const ElfW(Sym) *)*((const ElfW(Sym) **)(extra_info)); + __msan_unpoison(s, sizeof(ElfW(Sym))); + } else if (flags == RTLD_DL_LINKMAP) { + __msan_unpoison(extra_info, sizeof(void *)); + + struct link_map *map = *((struct link_map **)(extra_info)); + + // Walk forward + for (auto *ptr = map; ptr; ptr = ptr->l_next) { + __msan_unpoison(ptr, sizeof(struct link_map)); + if (ptr->l_name) + __msan_unpoison(ptr->l_name, internal_strlen(ptr->l_name) + 1); + } + + if (!map) + return; + + // Walk backward + for (auto *ptr = map->l_prev; ptr; ptr = ptr->l_prev) { + __msan_unpoison(ptr, sizeof(struct link_map)); + if (ptr->l_name) + __msan_unpoison(ptr->l_name, internal_strlen(ptr->l_name) + 1); + } + } +} + +} // 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 @@ -19,6 +19,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" @@ -1520,23 +1521,23 @@ return res; } -struct dlinfo { - char *dli_fname; - void *dli_fbase; - char *dli_sname; - void *dli_saddr; -}; - -INTERCEPTOR(int, dladdr, void *addr, dlinfo *info) { +INTERCEPTOR(int, dladdr, void *addr, void *info) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, dladdr, addr, info); int res = REAL(dladdr)(addr, info); + if (res != 0) + UnpoisonDllAddrInfo(info); + return res; +} + +INTERCEPTOR(int, dladdr1, void *addr, void *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); + UnpoisonDllAddrInfo(info); + UnpoisonDllAddr1ExtraInfo(extra_info, flags); } return res; } @@ -1788,6 +1789,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