diff options
author | Thurston Dang <thurston@google.com> | 2023-07-11 16:45:25 +0000 |
---|---|---|
committer | Thurston Dang <thurston@google.com> | 2023-07-11 20:05:54 +0000 |
commit | 9095d5c44bf7c36ddc48edb65c6d3a7b4ea8692f (patch) | |
tree | 4626c184e8c04524e47250d71d692536b979e598 /compiler-rt | |
parent | 4a63264daa5b68210f83c97705db5dc4cf227638 (diff) | |
download | llvm-9095d5c44bf7c36ddc48edb65c6d3a7b4ea8692f.zip llvm-9095d5c44bf7c36ddc48edb65c6d3a7b4ea8692f.tar.gz llvm-9095d5c44bf7c36ddc48edb65c6d3a7b4ea8692f.tar.bz2 |
Reland '[msan] Intercept dladdr1, and refactor dladdr'
Relanding with #if SANITIZER_GLIBC to avoid breaking FreeBSD.
Also incorporates Arthur's BUILD.gn fix (thanks!) from https://reviews.llvm.org/rGc1e283851772ba494113311405d48cfb883751d1
Original commit message:
This patch adds an msan interceptor for dladdr1 (with support for RTLD_DL_LINKMAP and RTLD_DL_SYMENT) and an accompanying test. It also adds a helper file, msan_dl.cpp, that contains UnpoisonDllAddrInfo (refactored out of the dladdr interceptor) and UnpoisonDllAddr1ExtraInfo.
Reviewed By: vitalybuka
Differential Revision: https://reviews.llvm.org/D154272
Diffstat (limited to 'compiler-rt')
-rw-r--r-- | compiler-rt/lib/msan/CMakeLists.txt | 2 | ||||
-rw-r--r-- | compiler-rt/lib/msan/msan_dl.cpp | 65 | ||||
-rw-r--r-- | compiler-rt/lib/msan/msan_dl.h | 30 | ||||
-rw-r--r-- | compiler-rt/lib/msan/msan_interceptors.cpp | 33 | ||||
-rw-r--r-- | compiler-rt/test/msan/dladdr1_test.c | 95 |
5 files changed, 212 insertions, 13 deletions
diff --git a/compiler-rt/lib/msan/CMakeLists.txt b/compiler-rt/lib/msan/CMakeLists.txt index b115f68..08b765d 100644 --- a/compiler-rt/lib/msan/CMakeLists.txt +++ b/compiler-rt/lib/msan/CMakeLists.txt @@ -5,6 +5,7 @@ set(MSAN_RTL_SOURCES 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 @@ set(MSAN_RTL_HEADERS 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.cpp b/compiler-rt/lib/msan/msan_dl.cpp new file mode 100644 index 0000000..624e3fe --- /dev/null +++ b/compiler-rt/lib/msan/msan_dl.cpp @@ -0,0 +1,65 @@ +//===-- 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 <dlfcn.h> +#include <elf.h> +#include <link.h> + +#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); +} + +#if SANITIZER_GLIBC +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); + } + } +} +#endif + +} // namespace __msan diff --git a/compiler-rt/lib/msan/msan_dl.h b/compiler-rt/lib/msan/msan_dl.h new file mode 100644 index 0000000..c0105ac --- /dev/null +++ b/compiler-rt/lib/msan/msan_dl.h @@ -0,0 +1,30 @@ +//===-- 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); + +#if SANITIZER_GLIBC +void UnpoisonDllAddr1ExtraInfo(void **extra_info, int flags); +#endif + +} // namespace __msan + +#endif // MSAN_DL_H diff --git a/compiler-rt/lib/msan/msan_interceptors.cpp b/compiler-rt/lib/msan/msan_interceptors.cpp index 6f57c33..18a54f8 100644 --- 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,26 +1521,31 @@ INTERCEPTOR(const char *, strsignal, int sig) { 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; +} + +#if SANITIZER_GLIBC +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; } +# define MSAN_MAYBE_INTERCEPT_DLADDR1 MSAN_INTERCEPT_FUNC(dladdr1) +#else +#define MSAN_MAYBE_INTERCEPT_DLADDR1 +#endif INTERCEPTOR(char *, dlerror, int fake) { void *ctx; @@ -1788,6 +1794,7 @@ void InitializeInterceptors() { MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT; INTERCEPT_FUNCTION(strsignal); INTERCEPT_FUNCTION(dladdr); + MSAN_MAYBE_INTERCEPT_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 index 0000000..dc5f6fd --- /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 <assert.h> +#include <dlfcn.h> +#include <elf.h> +#include <link.h> +#include <stdio.h> +#include <stdlib.h> + +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 |