[compiler-rt] [llvm] [sanitizer_common] Add experimental flag to tweak dlopen(<main program>) (PR #71715)

Thurston Dang via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 9 10:55:53 PST 2023


https://github.com/thurstond updated https://github.com/llvm/llvm-project/pull/71715

>From 2a73a5fba63e5dc769fb10166315f3c912bcf9fa Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Wed, 8 Nov 2023 17:50:29 +0000
Subject: [PATCH 1/6] [sanitizer_common] Add experimental flag to tweak
 dlopen(<main program>)

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.
---
 .../sanitizer_common_interceptors.inc         | 30 ++++++++++++++++++-
 .../lib/sanitizer_common/sanitizer_flags.inc  |  6 ++++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 80efaf54a0607f6..0f7d9da9d148443 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -6304,10 +6304,38 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
 #endif
 
 #if SANITIZER_INTERCEPT_DLOPEN_DLCLOSE
+// Returns 1 if key is a suffix of str, 0 otherwise
+static int internal_strcmp_suffix(const char *key, const char *str) {
+  if (!key || !str)
+    return 0;
+
+  if (internal_strlen(key) > internal_strlen(str))
+    return 0;
+
+  return !internal_strcmp(str + internal_strlen(str) - internal_strlen(key),
+                          key);
+}
+
+#  if SANITIZER_GLIBC
+extern char *__progname;
+#  endif
+
 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) {
+#  if SANITIZER_GLIBC
+    if (common_flags()->test_only_replace_dlopen_main_program &&
+        internal_strcmp_suffix(__progname, filename)) {
+      VPrintf(1, "dlopen interceptor: replacing %s because it matches %s\n",
+              filename, __progname);
+      filename = (char *)0;  // RTLD_DEFAULT
+    }
+#  endif
+    COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
+  }
+
   void *res = COMMON_INTERCEPTOR_DLOPEN(filename, flag);
   Symbolizer::GetOrInit()->InvalidateModuleList();
   COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
index 6148ae56067cae0..949bdbd148b6b89 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
@@ -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)")

>From da65373205833bfd82cd88adf0ed8f796f037d70 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Wed, 8 Nov 2023 18:29:32 +0000
Subject: [PATCH 2/6] Check validity of filename string before reading

---
 .../lib/sanitizer_common/sanitizer_common_interceptors.inc      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 0f7d9da9d148443..02167ad4c1c88e6 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -6325,6 +6325,7 @@ INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
   COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag);
 
   if (filename) {
+    COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
 #  if SANITIZER_GLIBC
     if (common_flags()->test_only_replace_dlopen_main_program &&
         internal_strcmp_suffix(__progname, filename)) {
@@ -6333,7 +6334,6 @@ INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
       filename = (char *)0;  // RTLD_DEFAULT
     }
 #  endif
-    COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
   }
 
   void *res = COMMON_INTERCEPTOR_DLOPEN(filename, flag);

>From c798bf13d30f1d6c03b367461eef97c3968b2d90 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Wed, 8 Nov 2023 19:11:50 +0000
Subject: [PATCH 3/6] internal_is_suffix fixes per Kirill's suggestion Added
 test case per Vitaly's suggestion

---
 .../sanitizer_common_interceptors.inc         | 14 ++++-----
 .../replace_dlopen_main_program_test.cpp      | 29 +++++++++++++++++++
 2 files changed, 36 insertions(+), 7 deletions(-)
 create mode 100644 compiler-rt/test/sanitizer_common/TestCases/replace_dlopen_main_program_test.cpp

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 02167ad4c1c88e6..f9952a729541a3c 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -6304,16 +6304,16 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
 #endif
 
 #if SANITIZER_INTERCEPT_DLOPEN_DLCLOSE
-// Returns 1 if key is a suffix of str, 0 otherwise
-static int internal_strcmp_suffix(const char *key, const char *str) {
-  if (!key || !str)
+// Returns 1 if 'suffix' matches the end of 'str', 0 otherwise
+static int internal_is_suffix(const char *suffix, const char *str) {
+  if (!suffix || !str)
     return 0;
 
-  if (internal_strlen(key) > internal_strlen(str))
+  if (internal_strlen(suffix) > internal_strlen(str))
     return 0;
 
-  return !internal_strcmp(str + internal_strlen(str) - internal_strlen(key),
-                          key);
+  return internal_strcmp(str + internal_strlen(str) - internal_strlen(suffix),
+                         suffix) == 0;
 }
 
 #  if SANITIZER_GLIBC
@@ -6328,7 +6328,7 @@ INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
     COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
 #  if SANITIZER_GLIBC
     if (common_flags()->test_only_replace_dlopen_main_program &&
-        internal_strcmp_suffix(__progname, filename)) {
+        internal_is_suffix(__progname, filename)) {
       VPrintf(1, "dlopen interceptor: replacing %s because it matches %s\n",
               filename, __progname);
       filename = (char *)0;  // RTLD_DEFAULT
diff --git a/compiler-rt/test/sanitizer_common/TestCases/replace_dlopen_main_program_test.cpp b/compiler-rt/test/sanitizer_common/TestCases/replace_dlopen_main_program_test.cpp
new file mode 100644
index 000000000000000..7e9e45836efbe53
--- /dev/null
+++ b/compiler-rt/test/sanitizer_common/TestCases/replace_dlopen_main_program_test.cpp
@@ -0,0 +1,29 @@
+// Test 'test_only_replace_dlopen_main_program' flag
+
+// RUN: %clangxx %s -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
+// REQUIRES: glibc
+
+// Does not intercept dlopen
+// UNSUPPORTED: hwasan, lsan, ubsan
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[]) {
+  // "If filename is NULL, then the returned handle is for the main program."
+  void *correct_handle = dlopen(NULL, RTLD_LAZY);
+
+  // Check that this is equivalent to dlopen(NULL, ...)
+  void *handle = dlopen(argv[0], RTLD_LAZY);
+  printf("dlopen(NULL,...): %p\n", correct_handle);
+  printf("dlopen(<main program>,...): %p\n", handle);
+  fflush(stdout);
+
+  if (handle != correct_handle)
+    return 1;
+
+  return 0;
+}

>From 17be36de8a6a4418fc231a77e2e1c254af5b9bcc Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Thu, 9 Nov 2023 18:06:21 +0000
Subject: [PATCH 4/6] Use dladdr to obtain the name of the main program, and
 then compare the canonicalized path against the canonicalized dlopen
 parameter.

Also updated test
---
 .../lib/sanitizer_common/CMakeLists.txt       |  2 +
 .../sanitizer_common_interceptors.inc         | 70 ++++++++++++++-----
 .../lib/sanitizer_common/sanitizer_dl.cpp     | 35 ++++++++++
 .../lib/sanitizer_common/sanitizer_dl.h       | 26 +++++++
 .../replace_dlopen_main_program_test.cpp      | 59 ++++++++++++++++
 .../replace_dlopen_main_program_test.cpp      | 29 --------
 .../compiler-rt/lib/sanitizer_common/BUILD.gn |  2 +
 7 files changed, 175 insertions(+), 48 deletions(-)
 create mode 100644 compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp
 create mode 100644 compiler-rt/lib/sanitizer_common/sanitizer_dl.h
 create mode 100644 compiler-rt/test/sanitizer_common/TestCases/Linux/replace_dlopen_main_program_test.cpp
 delete mode 100644 compiler-rt/test/sanitizer_common/TestCases/replace_dlopen_main_program_test.cpp

diff --git a/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
index 25304b71c0c7061..ce6d4cf80919b25 100644
--- a/compiler-rt/lib/sanitizer_common/CMakeLists.txt
+++ b/compiler-rt/lib/sanitizer_common/CMakeLists.txt
@@ -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
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
index f9952a729541a3c..a15da932e6052c0 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -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, ...) \
@@ -6304,21 +6305,24 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
 #endif
 
 #if SANITIZER_INTERCEPT_DLOPEN_DLCLOSE
-// Returns 1 if 'suffix' matches the end of 'str', 0 otherwise
-static int internal_is_suffix(const char *suffix, const char *str) {
-  if (!suffix || !str)
-    return 0;
+// Ordinary internal_readlink does not null-terminate strings.
+static int internal_readlink_with_null(const char *path, char *buf,
+                                       uptr bufsize) {
+  if (!buf) {
+    return -1;
+  }
 
-  if (internal_strlen(suffix) > internal_strlen(str))
-    return 0;
+  // Ensure it is a valid string even if internal_readlink fails
+  buf[0] = '\0';
 
-  return internal_strcmp(str + internal_strlen(str) - internal_strlen(suffix),
-                         suffix) == 0;
-}
+  int len = internal_readlink(path, buf, bufsize);
+  if (len >= 0 && len < (int)bufsize)
+    buf[len] = '\0';
 
-#  if SANITIZER_GLIBC
-extern char *__progname;
-#  endif
+  buf[bufsize - 1] = '\0';
+
+  return len;
+}
 
 INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
   void *ctx;
@@ -6326,14 +6330,42 @@ INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
 
   if (filename) {
     COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
-#  if SANITIZER_GLIBC
-    if (common_flags()->test_only_replace_dlopen_main_program &&
-        internal_is_suffix(__progname, filename)) {
+
+#  if !SANITIZER_DYNAMIC
+    VPrintf(1, "dlopen interceptor: filename: %s\n", filename);
+
+    char *filename_canonical = (char *)InternalAlloc(sizeof(char) * 640);
+    if (filename_canonical)
+      internal_readlink_with_null(filename, filename_canonical, 640);
+    VPrintf(1, "dlopen interceptor: filename (canonical): %s\n",
+            filename_canonical);
+
+    // We care about dlopen(<main program>,...), which will only happen
+    // for statically linked ASan.
+    const char *self_fname;
+    int ret = dladdr_self_fname(&self_fname);
+    VPrintf(1, "dlopen interceptor: dladdr_self_fname: %p %s\n",
+            (void *)self_fname, self_fname);
+
+    char *self_fname_canonical = (char *)InternalAlloc(sizeof(char) * 640);
+    if (self_fname_canonical)
+      internal_readlink_with_null(self_fname, self_fname_canonical, 640);
+    VPrintf(1, "dlopen interceptor: dladdr_self_fname (canonical): %s\n",
+            self_fname_canonical);
+
+    if (ret && common_flags()->test_only_replace_dlopen_main_program &&
+        (internal_strcmp(self_fname_canonical, filename_canonical) == 0)) {
+      // Our test (replace_dlopen_main_program_test.cpp) does not obtain the
+      // path name from dladdr(). We therefore need to compare string contents,
+      // not just the filename addresses.
       VPrintf(1, "dlopen interceptor: replacing %s because it matches %s\n",
-              filename, __progname);
+              filename, self_fname);
       filename = (char *)0;  // RTLD_DEFAULT
     }
-#  endif
+
+    InternalFree(filename_canonical);
+    InternalFree(self_fname_canonical);
+#  endif  // !SANITIZER_DYNAMIC
   }
 
   void *res = COMMON_INTERCEPTOR_DLOPEN(filename, flag);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp
new file mode 100644
index 000000000000000..978714358301035
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp
@@ -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;
+
+int dladdr_self_fname(const char **fname) {
+#if SANITIZER_GLIBC
+  Dl_info info;
+  int ret = dladdr((void *)&SanitizerToolName, &info);
+  *fname = info.dli_fname;
+  return ret;
+#else
+  *fname = nullptr;
+  return 0;
+#endif
+}
+
+}  // namespace __sanitizer
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_dl.h b/compiler-rt/lib/sanitizer_common/sanitizer_dl.h
new file mode 100644
index 000000000000000..8e375064267de8e
--- /dev/null
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_dl.h
@@ -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.
+int dladdr_self_fname(const char **fname);
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_DL_H
diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/replace_dlopen_main_program_test.cpp b/compiler-rt/test/sanitizer_common/TestCases/Linux/replace_dlopen_main_program_test.cpp
new file mode 100644
index 000000000000000..5750ad7f345c58f
--- /dev/null
+++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/replace_dlopen_main_program_test.cpp
@@ -0,0 +1,59 @@
+// 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;
+  }
+
+  // Check that this is equivalent to dlopen(NULL, ...)
+  test_handle = dlopen(argv[0], RTLD_LAZY);
+  printf("argv[0]: %s\n", argv[0]);
+  printf("dlopen(%s,...): %p\n", argv[0], test_handle);
+  fflush(stdout);
+
+  if (test_handle != correct_handle) {
+    printf("Error: handles do not match\n");
+    return 1;
+  }
+
+  return 0;
+}
diff --git a/compiler-rt/test/sanitizer_common/TestCases/replace_dlopen_main_program_test.cpp b/compiler-rt/test/sanitizer_common/TestCases/replace_dlopen_main_program_test.cpp
deleted file mode 100644
index 7e9e45836efbe53..000000000000000
--- a/compiler-rt/test/sanitizer_common/TestCases/replace_dlopen_main_program_test.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Test 'test_only_replace_dlopen_main_program' flag
-
-// RUN: %clangxx %s -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
-// REQUIRES: glibc
-
-// Does not intercept dlopen
-// UNSUPPORTED: hwasan, lsan, ubsan
-
-#include <dlfcn.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-int main(int argc, char *argv[]) {
-  // "If filename is NULL, then the returned handle is for the main program."
-  void *correct_handle = dlopen(NULL, RTLD_LAZY);
-
-  // Check that this is equivalent to dlopen(NULL, ...)
-  void *handle = dlopen(argv[0], RTLD_LAZY);
-  printf("dlopen(NULL,...): %p\n", correct_handle);
-  printf("dlopen(<main program>,...): %p\n", handle);
-  fflush(stdout);
-
-  if (handle != correct_handle)
-    return 1;
-
-  return 0;
-}
diff --git a/llvm/utils/gn/secondary/compiler-rt/lib/sanitizer_common/BUILD.gn b/llvm/utils/gn/secondary/compiler-rt/lib/sanitizer_common/BUILD.gn
index d6f18aec9f2113f..5a4a6f17cf311a6 100644
--- a/llvm/utils/gn/secondary/compiler-rt/lib/sanitizer_common/BUILD.gn
+++ b/llvm/utils/gn/secondary/compiler-rt/lib/sanitizer_common/BUILD.gn
@@ -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",

>From 3783ba1b6e6736da97e278d6d0d76b4d77e63041 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Thu, 9 Nov 2023 18:49:35 +0000
Subject: [PATCH 5/6] Simplify code, assuming that we don't need to
 canonicalize paths.

Use camelCase
---
 .../sanitizer_common_interceptors.inc         | 72 ++++++-------------
 .../lib/sanitizer_common/sanitizer_dl.cpp     | 12 ++--
 .../lib/sanitizer_common/sanitizer_dl.h       |  2 +-
 .../replace_dlopen_main_program_test.cpp      | 11 ---
 4 files changed, 28 insertions(+), 69 deletions(-)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
index a15da932e6052c0..b8c9247ed474ba9 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -6305,25 +6305,6 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
 #endif
 
 #if SANITIZER_INTERCEPT_DLOPEN_DLCLOSE
-// Ordinary internal_readlink does not null-terminate strings.
-static int internal_readlink_with_null(const char *path, char *buf,
-                                       uptr bufsize) {
-  if (!buf) {
-    return -1;
-  }
-
-  // Ensure it is a valid string even if internal_readlink fails
-  buf[0] = '\0';
-
-  int len = internal_readlink(path, buf, bufsize);
-  if (len >= 0 && len < (int)bufsize)
-    buf[len] = '\0';
-
-  buf[bufsize - 1] = '\0';
-
-  return len;
-}
-
 INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
   void *ctx;
   COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag);
@@ -6332,39 +6313,28 @@ INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
     COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
 
 #  if !SANITIZER_DYNAMIC
-    VPrintf(1, "dlopen interceptor: filename: %s\n", filename);
-
-    char *filename_canonical = (char *)InternalAlloc(sizeof(char) * 640);
-    if (filename_canonical)
-      internal_readlink_with_null(filename, filename_canonical, 640);
-    VPrintf(1, "dlopen interceptor: filename (canonical): %s\n",
-            filename_canonical);
-
-    // We care about dlopen(<main program>,...), which will only happen
-    // for statically linked ASan.
-    const char *self_fname;
-    int ret = dladdr_self_fname(&self_fname);
-    VPrintf(1, "dlopen interceptor: dladdr_self_fname: %p %s\n",
-            (void *)self_fname, self_fname);
-
-    char *self_fname_canonical = (char *)InternalAlloc(sizeof(char) * 640);
-    if (self_fname_canonical)
-      internal_readlink_with_null(self_fname, self_fname_canonical, 640);
-    VPrintf(1, "dlopen interceptor: dladdr_self_fname (canonical): %s\n",
-            self_fname_canonical);
-
-    if (ret && common_flags()->test_only_replace_dlopen_main_program &&
-        (internal_strcmp(self_fname_canonical, filename_canonical) == 0)) {
-      // Our test (replace_dlopen_main_program_test.cpp) does not obtain the
-      // path name from dladdr(). We therefore need to compare string contents,
-      // not just the filename addresses.
-      VPrintf(1, "dlopen interceptor: replacing %s because it matches %s\n",
-              filename, self_fname);
-      filename = (char *)0;  // RTLD_DEFAULT
+    // 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
+      }
     }
-
-    InternalFree(filename_canonical);
-    InternalFree(self_fname_canonical);
 #  endif  // !SANITIZER_DYNAMIC
   }
 
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp
index 978714358301035..e956e2150f63a20 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp
@@ -20,16 +20,16 @@
 namespace __sanitizer {
 extern const char *SanitizerToolName;
 
-int dladdr_self_fname(const char **fname) {
+const char *dladdrSelfFName(void) {
 #if SANITIZER_GLIBC
   Dl_info info;
   int ret = dladdr((void *)&SanitizerToolName, &info);
-  *fname = info.dli_fname;
-  return ret;
-#else
-  *fname = nullptr;
-  return 0;
+  if (ret) {
+    return info.dli_fname;
+  }
 #endif
+
+  return nullptr;
 }
 
 }  // namespace __sanitizer
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_dl.h b/compiler-rt/lib/sanitizer_common/sanitizer_dl.h
index 8e375064267de8e..a24beadd2991596 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_dl.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_dl.h
@@ -19,7 +19,7 @@ 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.
-int dladdr_self_fname(const char **fname);
+const char* dladdrSelfFName(void);
 
 }  // namespace __sanitizer
 
diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/replace_dlopen_main_program_test.cpp b/compiler-rt/test/sanitizer_common/TestCases/Linux/replace_dlopen_main_program_test.cpp
index 5750ad7f345c58f..72fff2719def2c4 100644
--- a/compiler-rt/test/sanitizer_common/TestCases/Linux/replace_dlopen_main_program_test.cpp
+++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/replace_dlopen_main_program_test.cpp
@@ -44,16 +44,5 @@ int main(int argc, char *argv[]) {
     return 1;
   }
 
-  // Check that this is equivalent to dlopen(NULL, ...)
-  test_handle = dlopen(argv[0], RTLD_LAZY);
-  printf("argv[0]: %s\n", argv[0]);
-  printf("dlopen(%s,...): %p\n", argv[0], test_handle);
-  fflush(stdout);
-
-  if (test_handle != correct_handle) {
-    printf("Error: handles do not match\n");
-    return 1;
-  }
-
   return 0;
 }

>From 85092c57f47b42e85610c202a3f873a9f6fe8958 Mon Sep 17 00:00:00 2001
From: Thurston Dang <thurston at google.com>
Date: Thu, 9 Nov 2023 18:55:24 +0000
Subject: [PATCH 6/6] Capitalized CamelCase

---
 .../sanitizer_common/sanitizer_common_interceptors.inc | 10 +++++-----
 compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp      |  2 +-
 compiler-rt/lib/sanitizer_common/sanitizer_dl.h        |  2 +-
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
index b8c9247ed474ba9..607ecae6808b72a 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -6323,15 +6323,15 @@ INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
     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);
+      const char *SelfFName = DladdrSelfFName();
+      VPrintf(1, "dlopen interceptor: DladdrSelfFName: %p %s\n",
+              (void *)SelfFName, SelfFName);
 
-      if (internal_strcmp(selfFName, filename) == 0) {
+      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, SelfFName);
         filename = (char *)0;  // RTLD_DEFAULT
       }
     }
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp
index e956e2150f63a20..65194c07b41530a 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_dl.cpp
@@ -20,7 +20,7 @@
 namespace __sanitizer {
 extern const char *SanitizerToolName;
 
-const char *dladdrSelfFName(void) {
+const char *DladdrSelfFName(void) {
 #if SANITIZER_GLIBC
   Dl_info info;
   int ret = dladdr((void *)&SanitizerToolName, &info);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_dl.h b/compiler-rt/lib/sanitizer_common/sanitizer_dl.h
index a24beadd2991596..ecde0664eb04950 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_dl.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_dl.h
@@ -19,7 +19,7 @@ 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);
+const char* DladdrSelfFName(void);
 
 }  // namespace __sanitizer
 



More information about the llvm-commits mailing list