dlmmap fix and always check for PaX MPROTECT on linux Also make EMUTRAMP experimental From Stefan Bühler https://github.com/libffi/libffi/pull/282
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
diff --git a/configure.ac b/configure.ac
index 05181c4..8edb7b6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -220,12 +220,17 @@ if test "x$libffi_cv_as_ptrauth" = xyes; then
[Define if your compiler supports pointer authentication.])
fi
-# On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC.
+# On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC;
+# if EMUTRAMP is active too ffi could try mapping without PROT_EXEC,
+# but the kernel needs to recognize the trampoline generated by ffi.
+# Otherwise fallback to double mmap trick.
AC_ARG_ENABLE(pax_emutramp,
- [ --enable-pax_emutramp enable pax emulated trampolines, for we can't use PROT_EXEC],
+ [ --enable-pax_emutramp enable pax emulated trampolines (experimental)],
if test "$enable_pax_emutramp" = "yes"; then
+ AC_MSG_WARN([EMUTRAMP is experimental only. Use --enable-pax_emutramp=experimental to enforce.])
+ elif test "$enable_pax_emutramp" = "experimental"; then
AC_DEFINE(FFI_MMAP_EXEC_EMUTRAMP_PAX, 1,
- [Define this if you want to enable pax emulated trampolines])
+ [Define this if you want to enable pax emulated trampolines (experimental)])
fi)
LT_SYS_SYMBOL_USCORE
diff --git a/src/closures.c b/src/closures.c
index db7ec94..6cbe2f0 100644
--- a/src/closures.c
+++ b/src/closures.c
@@ -141,14 +141,18 @@ ffi_tramp_is_present (__attribute__((unused)) void *ptr)
# endif
#endif
-#if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX
-# if defined(__linux__) && !defined(__ANDROID__)
-/* When defined to 1 check for SELinux and if SELinux is active,
- don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
- might cause audit messages. */
-# define FFI_MMAP_EXEC_SELINUX 1
-# endif
-#endif
+#if FFI_MMAP_EXEC_WRIT && defined(__linux__) && !defined(__ANDROID__)
+# if !defined FFI_MMAP_EXEC_SELINUX
+/* When defined to 1 check for SELinux and if SELinux is active,
+ don't attempt PROT_EXEC|PROT_WRITE mapping at all, as that
+ might cause audit messages. */
+# define FFI_MMAP_EXEC_SELINUX 1
+# endif /* !defined FFI_MMAP_EXEC_SELINUX */
+# if !defined FFI_MMAP_PAX
+/* Also check for PaX MPROTECT */
+# define FFI_MMAP_PAX 1
+# endif /* !defined FFI_MMAP_PAX */
+#endif FFI_MMAP_EXEC_WRIT && defined(__linux__) && !defined(__ANDROID__)
#if FFI_CLOSURES
@@ -479,14 +483,18 @@ selinux_enabled_check (void)
#endif /* !FFI_MMAP_EXEC_SELINUX */
-/* On PaX enable kernels that have MPROTECT enable we can't use PROT_EXEC. */
-#ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
+/* On PaX enable kernels that have MPROTECT enabled we can't use PROT_EXEC. */
+#if defined FFI_MMAP_PAX
#include <stdlib.h>
-static int emutramp_enabled = -1;
+enum {
+ PAX_MPROTECT = (1 << 0),
+ PAX_EMUTRAMP = (1 << 1),
+};
+static int cached_pax_flags = -1;
static int
-emutramp_enabled_check (void)
+pax_flags_check (void)
{
char *buf = NULL;
size_t len = 0;
@@ -500,9 +508,10 @@ emutramp_enabled_check (void)
while (getline (&buf, &len, f) != -1)
if (!strncmp (buf, "PaX:", 4))
{
- char emutramp;
- if (sscanf (buf, "%*s %*c%c", &emutramp) == 1)
- ret = (emutramp == 'E');
+ if (NULL != strchr (buf + 4, 'M'))
+ ret |= PAX_MPROTECT;
+ if (NULL != strchr (buf + 4, 'E'))
+ ret |= PAX_EMUTRAMP;
break;
}
free (buf);
@@ -510,9 +519,13 @@ emutramp_enabled_check (void)
return ret;
}
-#define is_emutramp_enabled() (emutramp_enabled >= 0 ? emutramp_enabled \
- : (emutramp_enabled = emutramp_enabled_check ()))
-#endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
+#define get_pax_flags() (cached_pax_flags >= 0 ? cached_pax_flags \
+ : (cached_pax_flags = pax_flags_check ()))
+#define has_pax_flags(flags) ((flags) == ((flags) & get_pax_flags ()))
+#define is_mprotect_enabled() (has_pax_flags (PAX_MPROTECT))
+#define is_emutramp_enabled() (has_pax_flags (PAX_EMUTRAMP))
+
+#endif /* defined FFI_MMAP_PAX */
#elif defined (__CYGWIN__) || defined(__INTERIX)
@@ -523,9 +536,10 @@ emutramp_enabled_check (void)
#endif /* !defined(X86_WIN32) && !defined(X86_WIN64) */
-#ifndef FFI_MMAP_EXEC_EMUTRAMP_PAX
-#define is_emutramp_enabled() 0
-#endif /* FFI_MMAP_EXEC_EMUTRAMP_PAX */
+#if !defined FFI_MMAP_PAX
+# define is_mprotect_enabled() 0
+# define is_emutramp_enabled() 0
+#endif /* !defined FFI_MMAP_PAX */
/* Declare all functions defined in dlmalloc.c as static. */
static void *dlmalloc(size_t);
@@ -890,13 +904,23 @@ dlmmap (void *start, size_t length, int prot,
return ptr;
}
- if (execfd == -1 && is_emutramp_enabled ())
+ /* -1 != execfd hints that we already decided to use dlmmap_locked
+ last time. */
+ if (execfd == -1 && is_mprotect_enabled ())
{
- ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
- return ptr;
+#ifdef FFI_MMAP_EXEC_EMUTRAMP_PAX
+ if (is_emutramp_enabled ())
+ {
+ /* emutramp requires the kernel recognizing the trampoline pattern
+ generated by ffi_prep_closure_loc; there is no way to test
+ in advance whether this will work, so this is experimental. */
+ ptr = mmap (start, length, prot & ~PROT_EXEC, flags, fd, offset);
+ return ptr;
+ }
+#endif
+ /* fallback to dlmmap_locked. */
}
-
- if (execfd == -1 && !is_selinux_enabled ())
+ else if (execfd == -1 && !is_selinux_enabled ())
{
ptr = mmap (start, length, prot | PROT_EXEC, flags, fd, offset);
@@ -909,16 +933,11 @@ dlmmap (void *start, size_t length, int prot,
MREMAP_DUP and prot at this point. */
}
- if (execsize == 0 || execfd == -1)
- {
- pthread_mutex_lock (&open_temp_exec_file_mutex);
- ptr = dlmmap_locked (start, length, prot, flags, offset);
- pthread_mutex_unlock (&open_temp_exec_file_mutex);
-
- return ptr;
- }
+ pthread_mutex_lock (&open_temp_exec_file_mutex);
+ ptr = dlmmap_locked (start, length, prot, flags, offset);
+ pthread_mutex_unlock (&open_temp_exec_file_mutex);
- return dlmmap_locked (start, length, prot, flags, offset);
+ return ptr;
}
/* Release memory at the given address, as well as the corresponding