diff --git a/configure.ac b/configure.ac index 77fc8c89cdf40..b413af0f09940 100644 --- a/configure.ac +++ b/configure.ac @@ -575,6 +575,7 @@ AC_CHECK_FUNCS(m4_normalize([ mmap nice nl_langinfo + pkey_mprotect poll pthread_jit_write_protect_np putenv diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 3ffb669e84742..96edcd14aacaa 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -30,9 +30,19 @@ #include "Zend/zend_observer.h" #include "zend_smart_str.h" #include "jit/zend_jit.h" +#if __has_include() +# include +#endif #ifdef HAVE_JIT +#if defined(HAVE_PKEY_MPROTECT) && defined(PKEY_DISABLE_WRITE) +# define ZEND_JIT_USE_PKEYS +# ifndef PKEY_DISABLE_EXECUTE +# define PKEY_DISABLE_EXECUTE 0 +# endif +#endif + #include "Optimizer/zend_func_info.h" #include "Optimizer/zend_ssa.h" #include "Optimizer/zend_inference.h" @@ -88,6 +98,10 @@ static void **dasm_ptr = NULL; static size_t dasm_size = 0; +#ifdef ZEND_JIT_USE_PKEYS +static int pkey = 0; /* Memory Protection Key */ +#endif + static zend_long jit_bisect_pos = 0; static zend_vm_opcode_handler_t zend_jit_runtime_jit_handler = NULL; @@ -104,6 +118,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV zend_runtime_jit(ZEND_O static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV zend_runtime_jit(ZEND_OPCODE_HANDLER_ARGS); #endif +static void zend_jit_protect_init(void); + static int zend_jit_trace_op_len(const zend_op *opline); static int zend_jit_trace_may_exit(const zend_op_array *op_array, const zend_op *opline); static uint32_t _zend_jit_trace_get_exit_point(const zend_op *to_opline, uint32_t flags ZEND_FILE_LINE_DC); @@ -3515,10 +3531,80 @@ int zend_jit_script(zend_script *script) return FAILURE; } +static void zend_jit_protect_init(void) +{ +#ifdef HAVE_MPROTECT +# ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP + if (zend_write_protect) { + pthread_jit_write_protect_np(1); + } +# endif + + if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) { + if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) { + fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno)); + } + return; + } + +# ifdef ZEND_JIT_USE_PKEYS + pkey = pkey_alloc(0, PKEY_DISABLE_WRITE); + if (pkey < 0) { + zend_accel_error(ACCEL_LOG_DEBUG, "zend_jit_protect_init: pkey_alloc() failed [%d] %s", errno, strerror(errno)); + pkey = 0; + } else if (pkey_mprotect(dasm_buf, dasm_size, PROT_READ | PROT_WRITE | PROT_EXEC, pkey) != 0) { + zend_accel_error(ACCEL_LOG_DEBUG, "zend_jit_protect_init: pkey_mprotect() failed [%d] %s", errno, strerror(errno)); + pkey = 0; + } else { + return; + } +# endif + + if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_EXEC) != 0) { + fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno)); + } + +#elif defined(_WIN32) + if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) { + DWORD old; + + if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READWRITE, &old)) { + DWORD err = GetLastError(); + char *msg = php_win32_error_to_msg(err); + fprintf(stderr, "VirtualProtect() failed [%lu] %s\n", err, msg); + php_win32_error_msg_free(msg); + } + } else { + DWORD old; + + if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READ, &old)) { + DWORD err = GetLastError(); + char *msg = php_win32_error_to_msg(err); + fprintf(stderr, "VirtualProtect() failed [%lu] %s\n", err, msg); + php_win32_error_msg_free(msg); + } + } +#endif +} + void zend_jit_unprotect(void) { #ifdef HAVE_MPROTECT if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) { +# ifdef ZEND_JIT_USE_PKEYS + if (pkey) { +# ifdef ZTS + int restrictions = 0; +# else + int restrictions = PKEY_DISABLE_EXECUTE; +# endif + if (pkey_set(pkey, restrictions) != 0) { + ZEND_UNREACHABLE(); + } + return; + } +# endif + int opts = PROT_READ | PROT_WRITE; #ifdef ZTS #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP @@ -3554,6 +3640,15 @@ void zend_jit_protect(void) { #ifdef HAVE_MPROTECT if (!(JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP))) { +# ifdef ZEND_JIT_USE_PKEYS + if (pkey) { + if (pkey_set(pkey, PKEY_DISABLE_WRITE) != 0) { + ZEND_UNREACHABLE(); + } + return; + } +# endif + #ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP if (zend_write_protect) { pthread_jit_write_protect_np(1); @@ -3787,42 +3882,7 @@ void zend_jit_startup(void *buf, size_t size, bool reattached) dasm_size = size; dasm_ptr = dasm_end = (void*)(((char*)dasm_buf) + size - sizeof(*dasm_ptr) * 2); -#ifdef HAVE_MPROTECT -#ifdef HAVE_PTHREAD_JIT_WRITE_PROTECT_NP - if (zend_write_protect) { - pthread_jit_write_protect_np(1); - } -#endif - if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) { - if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) { - fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno)); - } - } else { - if (mprotect(dasm_buf, dasm_size, PROT_READ | PROT_EXEC) != 0) { - fprintf(stderr, "mprotect() failed [%d] %s\n", errno, strerror(errno)); - } - } -#elif defined(_WIN32) - if (JIT_G(debug) & (ZEND_JIT_DEBUG_GDB|ZEND_JIT_DEBUG_PERF_DUMP)) { - DWORD old; - - if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READWRITE, &old)) { - DWORD err = GetLastError(); - char *msg = php_win32_error_to_msg(err); - fprintf(stderr, "VirtualProtect() failed [%lu] %s\n", err, msg); - php_win32_error_msg_free(msg); - } - } else { - DWORD old; - - if (!VirtualProtect(dasm_buf, dasm_size, PAGE_EXECUTE_READ, &old)) { - DWORD err = GetLastError(); - char *msg = php_win32_error_to_msg(err); - fprintf(stderr, "VirtualProtect() failed [%lu] %s\n", err, msg); - php_win32_error_msg_free(msg); - } - } -#endif + zend_jit_protect_init(); if (!reattached) { zend_jit_unprotect(); @@ -3880,6 +3940,12 @@ void zend_jit_shutdown(void) dasm_buf = NULL; dasm_end = NULL; dasm_size = 0; + +#ifdef ZEND_JIT_USE_PKEYS + if (pkey) { + pkey_free(pkey); + } +#endif } static void zend_jit_reset_counters(void) diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c index 80ef36b8749d9..2f9356591d61e 100644 --- a/ext/opcache/zend_shared_alloc.c +++ b/ext/opcache/zend_shared_alloc.c @@ -44,6 +44,10 @@ # include "sys/mman.h" #endif +#if defined(HAVE_PKEY_MPROTECT) && defined(PKEY_DISABLE_WRITE) +# define ZEND_ACCEL_USE_PKEYS +#endif + #define SEM_FILENAME_PREFIX ".ZendSem." #define S_H(s) g_shared_alloc_handler->s @@ -79,6 +83,12 @@ static const zend_shared_memory_handler_entry handler_table[] = { { NULL, NULL} }; +#ifdef ZEND_ACCEL_USE_PKEYS +static int pkey = 0; /* Memory Protection Key */ +#endif + +static void zend_accel_shared_protect_init(void); + #ifndef ZEND_WIN32 void zend_shared_alloc_create_lock(char *lockfile_path) { @@ -260,6 +270,8 @@ int zend_shared_alloc_startup(size_t requested_size, size_t reserved_size) ZSMMG(shared_segments)[i]->end = ZSMMG(shared_segments)[i]->size; } + zend_accel_shared_protect_init(); + shared_segments_array_size = ZSMMG(shared_segments_count) * S_H(segment_type_size)(); /* move shared_segments and shared_free to shared memory */ @@ -342,6 +354,13 @@ void zend_shared_alloc_shutdown(void) # ifdef ZTS tsrm_mutex_free(zts_lock); # endif + +# ifdef ZEND_ACCEL_USE_PKEYS + if (pkey) { + pkey_free(pkey); + } +# endif + #endif } @@ -628,9 +647,38 @@ const char *zend_accel_get_shared_model(void) return g_shared_model; } +static void zend_accel_shared_protect_init(void) +{ +#ifdef ZEND_ACCEL_USE_PKEYS + pkey = pkey_alloc(0, 0); + if (pkey < 0) { + zend_accel_error(ACCEL_LOG_DEBUG, "zend_accel_shared_protect_init: pkey_alloc() failed [%d] %s", errno, strerror(errno)); + pkey = 0; + return; + } + + for (int i = 0; i < ZSMMG(shared_segments_count); i++) { + if (pkey_mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->end, PROT_READ | PROT_WRITE, pkey) != 0) { + zend_accel_error(ACCEL_LOG_DEBUG, "zend_accel_shared_protect_init: pkey_mprotect() failed [%d] %s", errno, strerror(errno)); + pkey = 0; + break; + } + } +#endif +} + void zend_accel_shared_protect(bool protected) { -#ifdef HAVE_MPROTECT +#ifdef ZEND_ACCEL_USE_PKEYS + if (pkey) { + if (pkey_set(pkey, protected ? PKEY_DISABLE_WRITE : 0) != 0) { + ZEND_UNREACHABLE(); + } + return; + } +#endif + +#if defined(HAVE_MPROTECT) int i; if (!smm_shared_globals) {