Skip to content
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

Merged
merged 1 commit into from
Nov 27, 2023

Conversation

DimitryAndric
Copy link
Collaborator

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.

@llvmbot
Copy link
Collaborator

llvmbot commented Nov 26, 2023

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Dimitry Andric (DimitryAndric)

Changes

In the FreeBSD base system, re-executing the main binary when ASLR is detected was implemented in the following commits:

  • freebsd/freebsd-src@7cafe89f9ce33
  • freebsd/freebsd-src@96fe7c8ab0f65
  • freebsd/freebsd-src@930a7c2ac67e1
  • freebsd/freebsd-src@0a736f0a6aeb0
  • freebsd/freebsd-src@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.


Full diff: https://github.com/llvm/llvm-project/pull/73439.diff

2 Files Affected:

  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp (+6-3)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp (+11-1)
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,

Copy link

github-actions bot commented Nov 26, 2023

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

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;

@DimitryAndric
Copy link
Collaborator Author

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>
Copy link
Collaborator

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> ?

Copy link
Collaborator Author

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).

@vitalybuka
Copy link
Collaborator

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.

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.
@DimitryAndric DimitryAndric merged commit 7440e4e into llvm:main Nov 27, 2023
2 of 3 checks passed
@DimitryAndric
Copy link
Collaborator Author

See #73573 for the reformatting of sanitizer_linux sources.

freebsd-git pushed a commit to freebsd/freebsd-src that referenced this pull request Nov 28, 2023
This is to sync the code with upstream, see:
See llvm/llvm-project#73439 (comment)

Fixes:		4c9a0ad
MFC after:	3 days
freebsd-git pushed a commit to freebsd/freebsd-src that referenced this pull request Dec 2, 2023
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)
freebsd-git pushed a commit to freebsd/freebsd-src that referenced this pull request Dec 2, 2023
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)
bsdjhb pushed a commit to bsdjhb/cheribsd that referenced this pull request Mar 4, 2024
This is to sync the code with upstream, see:
See llvm/llvm-project#73439 (comment)

Fixes:		4c9a0ad
MFC after:	3 days
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants