[compiler-rt] [sanitizer_common] Add experimental flag to tweak dlopen(<main program>) (PR #71715)
Thurston Dang via llvm-commits
llvm-commits at lists.llvm.org
Wed Nov 8 11:17:03 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/3] [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/3] 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/3] 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;
+}
More information about the llvm-commits
mailing list