-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[sanitizer] Add re-execution on FreeBSD when ASLR is detected #73439
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[sanitizer] Add re-execution on FreeBSD when ASLR is detected #73439
Conversation
@llvm/pr-subscribers-compiler-rt-sanitizer Author: Dimitry Andric (DimitryAndric) ChangesIn the FreeBSD base system, re-executing the main binary when ASLR is detected was implemented in the following commits:
Squash all these to bring them into upstream compiler-rt. When ASLR is detected to be enabled, this first force-disables ASLR for the current process, then calls ReExec(). The ReExec() function gets a FreeBSD specific implementation for finding the path of the executed program, via the ELF auxiliary vector. This is done without calling into the regular elf_aux_info(3) function, as that makes use of several already-intercepted functions. Full diff: https://github.com/llvm/llvm-project/pull/73439.diff 2 Files Affected:
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
index d2b3b63f3a7a3bd..8759d96609e567c 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
@@ -2323,9 +2323,12 @@ void CheckASLR() {
return;
}
if ((aslr_status & PROC_ASLR_ACTIVE) != 0) {
- Printf("This sanitizer is not compatible with enabled ASLR "
- "and binaries compiled with PIE\n");
- Die();
+ VReport(1, "This sanitizer is not compatible with enabled ASLR "
+ "and binaries compiled with PIE\n"
+ "ASLR will be disabled and the program re-executed.\n");
+ int aslr_ctl = PROC_ASLR_FORCE_DISABLE;
+ CHECK_NE(internal_procctl(P_PID, 0, PROC_ASLR_CTL, &aslr_ctl), -1);
+ ReExec();
}
# elif SANITIZER_PPC64V2
// Disable ASLR for Linux PPC64LE.
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp
index fcfaa0c36c2251b..ea816eacd980249 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp
@@ -47,13 +47,16 @@
#if SANITIZER_FREEBSD
#include <pthread_np.h>
+#include <stdlib.h>
#include <osreldate.h>
+#include <sys/auxv.h>
#include <sys/sysctl.h>
#define pthread_getattr_np pthread_attr_get_np
// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
// that, it was never implemented. So just define it to zero.
#undef MAP_NORESERVE
#define MAP_NORESERVE 0
+extern const Elf_Auxinfo *__elf_aux_vector;
#endif
#if SANITIZER_NETBSD
@@ -941,7 +944,14 @@ u64 MonotonicNanoTime() {
void ReExec() {
const char *pathname = "/proc/self/exe";
-#if SANITIZER_NETBSD
+#if SANITIZER_FREEBSD
+ for (const auto *aux = __elf_aux_vector; aux->a_type != AT_NULL; aux++) {
+ if (aux->a_type == AT_EXECPATH) {
+ pathname = static_cast<const char *>(aux->a_un.a_ptr);
+ break;
+ }
+ }
+#elif SANITIZER_NETBSD
static const int name[] = {
CTL_KERN,
KERN_PROC_ARGS,
|
You can test this locally with the following command:git-clang-format --diff f90f036efbe6879ecf1c9ff8cf0fb956b5ceb877 4fa5031b5419f6d1eaf31334461bb945e72f6b13 -- compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp View the diff from clang-format here.diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
index 8759d96609..8dd9ee449a 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp
@@ -2323,9 +2323,10 @@ void CheckASLR() {
return;
}
if ((aslr_status & PROC_ASLR_ACTIVE) != 0) {
- VReport(1, "This sanitizer is not compatible with enabled ASLR "
- "and binaries compiled with PIE\n"
- "ASLR will be disabled and the program re-executed.\n");
+ VReport(1,
+ "This sanitizer is not compatible with enabled ASLR "
+ "and binaries compiled with PIE\n"
+ "ASLR will be disabled and the program re-executed.\n");
int aslr_ctl = PROC_ASLR_FORCE_DISABLE;
CHECK_NE(internal_procctl(P_PID, 0, PROC_ASLR_CTL, &aslr_ctl), -1);
ReExec();
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp
index b41f778ea9..9072f94fd3 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp
@@ -46,35 +46,35 @@
#endif
#if SANITIZER_FREEBSD
-#include <pthread_np.h>
-#include <osreldate.h>
-#include <sys/auxv.h>
-#include <sys/sysctl.h>
-#define pthread_getattr_np pthread_attr_get_np
+# include <osreldate.h>
+# include <pthread_np.h>
+# include <sys/auxv.h>
+# include <sys/sysctl.h>
+# define pthread_getattr_np pthread_attr_get_np
// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
// that, it was never implemented. So just define it to zero.
#undef MAP_NORESERVE
#define MAP_NORESERVE 0
extern const Elf_Auxinfo *__elf_aux_vector;
-#endif
+# endif
-#if SANITIZER_NETBSD
-#include <sys/sysctl.h>
-#include <sys/tls.h>
-#include <lwp.h>
-#endif
+# if SANITIZER_NETBSD
+# include <sys/sysctl.h>
+# include <sys/tls.h>
+# include <lwp.h>
+# endif
-#if SANITIZER_SOLARIS
-#include <stddef.h>
-#include <stdlib.h>
-#include <thread.h>
-#endif
+# if SANITIZER_SOLARIS
+# include <stddef.h>
+# include <stdlib.h>
+# include <thread.h>
+# endif
-#if SANITIZER_ANDROID
-#include <android/api-level.h>
-#if !defined(CPU_COUNT) && !defined(__aarch64__)
-#include <dirent.h>
-#include <fcntl.h>
+# if SANITIZER_ANDROID
+# include <android/api-level.h>
+# if !defined(CPU_COUNT) && !defined(__aarch64__)
+# include <dirent.h>
+# include <fcntl.h>
struct __sanitizer::linux_dirent {
long d_ino;
off_t d_off;
@@ -943,14 +943,14 @@ u64 MonotonicNanoTime() {
void ReExec() {
const char *pathname = "/proc/self/exe";
-#if SANITIZER_FREEBSD
+# if SANITIZER_FREEBSD
for (const auto *aux = __elf_aux_vector; aux->a_type != AT_NULL; aux++) {
if (aux->a_type == AT_EXECPATH) {
pathname = static_cast<const char *>(aux->a_un.a_ptr);
break;
}
}
-#elif SANITIZER_NETBSD
+# elif SANITIZER_NETBSD
static const int name[] = {
CTL_KERN,
KERN_PROC_ARGS,
@@ -963,14 +963,14 @@ void ReExec() {
len = sizeof(path);
if (internal_sysctl(name, ARRAY_SIZE(name), path, &len, NULL, 0) != -1)
pathname = path;
-#elif SANITIZER_SOLARIS
+# elif SANITIZER_SOLARIS
pathname = getexecname();
CHECK_NE(pathname, NULL);
-#elif SANITIZER_USE_GETAUXVAL
+# elif SANITIZER_USE_GETAUXVAL
// Calling execve with /proc/self/exe sets that as $EXEC_ORIGIN. Binaries that
// rely on that will fail to load shared libraries. Query AT_EXECFN instead.
pathname = reinterpret_cast<const char *>(getauxval(AT_EXECFN));
-#endif
+# endif
uptr rv = internal_execve(pathname, GetArgv(), GetEnviron());
int rverrno;
|
I am going to assume that the existing code formatting is going to be kept. Let me know if the clang-format messages are considered "fatal", then I will run the whole enchilada through a reformatting first, and commit that, since I dislike mixing formatting changes and functional changes. |
@@ -47,13 +47,16 @@ | |||
|
|||
#if SANITIZER_FREEBSD | |||
#include <pthread_np.h> | |||
#include <stdlib.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need <stdlib.h> ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that can actually be removed, it is a leftover from an older version where we called realpath(3) on the result. That code was copied from llvm's GetExecutablePath(), but in this case calling realpath is not necessary, and in fact might be counterproductive if you invoke an executable via a symlink (and expect argv[0] to be the alternative name).
Would you like to create a separate PR to reformat the file. |
…3439) In the FreeBSD base system, re-executing the main binary when ASLR is detected was implemented in the following commits: * https://cgit.freebsd.org/src/commit/?id=7cafe89f9ce33 * https://cgit.freebsd.org/src/commit/?id=96fe7c8ab0f65 * https://cgit.freebsd.org/src/commit/?id=930a7c2ac67e1 * https://cgit.freebsd.org/src/commit/?id=0a736f0a6aeb0 * https://cgit.freebsd.org/src/commit/?id=4c9a0adad1826 Squash all these to bring them into upstream compiler-rt. When ASLR is detected to be enabled, this first force-disables ASLR for the current process, then calls ReExec(). The ReExec() function gets a FreeBSD specific implementation for finding the path of the executed program, via the ELF auxiliary vector. This is done without calling into the regular elf_aux_info(3) function, as that makes use of several already-intercepted functions.
3f99711
to
4fa5031
Compare
See #73573 for the reformatting of sanitizer_linux sources. |
This is to sync the code with upstream, see: See llvm/llvm-project#73439 (comment) Fixes: 4c9a0ad MFC after: 3 days
This is to sync the code with upstream, see: See llvm/llvm-project#73439 (comment) Fixes: 4c9a0ad MFC after: 3 days (cherry picked from commit d203302)
This is to sync the code with upstream, see: See llvm/llvm-project#73439 (comment) Fixes: 4c9a0ad MFC after: 3 days (cherry picked from commit d203302)
This is to sync the code with upstream, see: See llvm/llvm-project#73439 (comment) Fixes: 4c9a0ad MFC after: 3 days
In the FreeBSD base system, re-executing the main binary when ASLR is detected was implemented in the following commits:
Squash all these to bring them into upstream compiler-rt.
When ASLR is detected to be enabled, this first force-disables ASLR for the current process, then calls ReExec(). The ReExec() function gets a FreeBSD specific implementation for finding the path of the executed program, via the ELF auxiliary vector. This is done without calling into the regular elf_aux_info(3) function, as that makes use of several already-intercepted functions.