From 757876336c183f5b20b6620d674cc9817fd0d280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Wed, 7 Sep 2016 15:50:54 +0200 Subject: [PATCH 2/2] always check for PaX MPROTECT on linux, make EMUTRAMP experimental - ffi_prep_closure_loc doesn't necessarily generate trampolines recognized by PaX EMUTRAMP handler; there is no way to check before, and it isn't working on x86-64 right now -> experimental - if MPROTECT is enabled use the same workaround as is used for SELinux (double mmap()) --- configure.ac | 11 +++++++--- src/closures.c | 68 +++++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 25 deletions(-) Index: b/configure.ac =================================================================== --- a/configure.ac +++ b/configure.ac @@ -439,12 +439,17 @@ if test x$TARGET = xX86 || test x$TARGET fi 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 Index: b/src/closures.c =================================================================== --- a/src/closures.c +++ b/src/closures.c @@ -53,14 +53,18 @@ # endif #endif -#if FFI_MMAP_EXEC_WRIT && !defined FFI_MMAP_EXEC_SELINUX -# ifdef __linux__ +#if FFI_MMAP_EXEC_WRIT && defined __linux__ +# 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 -#endif +# 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__ */ #if FFI_CLOSURES @@ -172,14 +176,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 -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; @@ -193,9 +201,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); @@ -203,9 +212,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) @@ -216,9 +229,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); @@ -525,13 +539,23 @@ dlmmap (void *start, size_t length, int printf ("mapping in %zi\n", length); #endif - 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);