Reland '[msan] Intercept dladdr1, and refactor dladdr'
authorThurston Dang <thurston@google.com>
Tue, 11 Jul 2023 16:45:25 +0000 (16:45 +0000)
committerThurston Dang <thurston@google.com>
Tue, 11 Jul 2023 20:05:54 +0000 (20:05 +0000)
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

compiler-rt/lib/msan/CMakeLists.txt
compiler-rt/lib/msan/msan_dl.cpp [new file with mode: 0644]
compiler-rt/lib/msan/msan_dl.h [new file with mode: 0644]
compiler-rt/lib/msan/msan_interceptors.cpp
compiler-rt/test/msan/dladdr1_test.c [new file with mode: 0644]
llvm/utils/gn/secondary/compiler-rt/lib/msan/BUILD.gn

index b115f68..08b765d 100644 (file)
@@ -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 (file)
index 0000000..624e3fe
--- /dev/null
@@ -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 (file)
index 0000000..c0105ac
--- /dev/null
@@ -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
index 6f57c33..18a54f8 100644 (file)
@@ -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 (file)
index 0000000..dc5f6fd
--- /dev/null
@@ -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
index 83539ac..b2ee2f6 100644 (file)
@@ -25,6 +25,8 @@ static_library("msan") {
     "msan_allocator.h",
     "msan_chained_origin_depot.cpp",
     "msan_chained_origin_depot.h",
+    "msan_dl.cpp",
+    "msan_dl.h",
     "msan_flags.h",
     "msan_flags.inc",
     "msan_interceptors.cpp",