diff --git a/configure.ac b/configure.ac index b5dcf0da17f8..fc93eeb16a87 100644 --- a/configure.ac +++ b/configure.ac @@ -579,6 +579,7 @@ AC_CHECK_FUNCS([ mmap nice nl_langinfo + pkey_mprotect poll pthread_jit_write_protect_np putenv diff --git a/ext/opcache/ZendAccelerator.h b/ext/opcache/ZendAccelerator.h index 5fd34441c402..c56b24ad2724 100644 --- a/ext/opcache/ZendAccelerator.h +++ b/ext/opcache/ZendAccelerator.h @@ -46,6 +46,11 @@ # include #endif +#ifdef HAVE_PKEY_MPROTECT +/* PKEY_DISABLE_WRITE */ +# include "sys/mman.h" +#endif + #include "zend_extensions.h" #include "zend_compile.h" #include "zend_API.h" @@ -358,6 +363,10 @@ zend_op_array *zend_accel_compile_pfa(zend_ast *ast, END_EXTERN_C() +#if defined(HAVE_PKEY_MPROTECT) && defined(PKEY_DISABLE_WRITE) +# define ZEND_ACCEL_USE_PKEYS +#endif + /* memory write protection */ #define SHM_PROTECT() \ do { \ diff --git a/ext/opcache/tests/zzz_basic_logging.phpt b/ext/opcache/tests/zzz_basic_logging.phpt index 88290f6a169d..ff5b0486d661 100644 --- a/ext/opcache/tests/zzz_basic_logging.phpt +++ b/ext/opcache/tests/zzz_basic_logging.phpt @@ -29,7 +29,7 @@ opcache_reset(); echo "Opcache reset"; ?> --EXPECTF-- -%s Message Cached script '%sbasic_logging%s' +%r[^:]+: Debug zend_accel_shared_protect_init: pkey_alloc\(\) failed .*\n|%r%s Message Cached script '%sbasic_logging%s' Foo Bar %s Debug Restart Scheduled! Reason: user Opcache reset diff --git a/ext/opcache/zend_shared_alloc.c b/ext/opcache/zend_shared_alloc.c index 3d6d2a840c33..c275968f5c82 100644 --- a/ext/opcache/zend_shared_alloc.c +++ b/ext/opcache/zend_shared_alloc.c @@ -82,6 +82,12 @@ static const zend_shared_memory_handler_entry handler_table[] = { { NULL, NULL} }; +#ifdef ZEND_ACCEL_USE_PKEYS +int zend_accel_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) { @@ -268,6 +274,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 */ @@ -350,6 +358,13 @@ void zend_shared_alloc_shutdown(void) # ifdef ZTS tsrm_mutex_free(zts_lock); # endif + +# ifdef ZEND_ACCEL_USE_PKEYS + if (zend_accel_pkey) { + pkey_free(zend_accel_pkey); + } +# endif + #endif #ifdef ZTS tsrm_mutex_free(zts_protect_lock); @@ -639,8 +654,39 @@ 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 + if (ZCG(accel_directives).protect_memory) { + zend_accel_pkey = pkey_alloc(0, 0); + if (zend_accel_pkey < 0) { + zend_accel_error(ACCEL_LOG_DEBUG, "zend_accel_shared_protect_init: pkey_alloc() failed [%d] %s", errno, strerror(errno)); + zend_accel_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, zend_accel_pkey) != 0) { + zend_accel_error(ACCEL_LOG_DEBUG, "zend_accel_shared_protect_init: pkey_mprotect() failed [%d] %s", errno, strerror(errno)); + zend_accel_pkey = 0; + break; + } + } + } +#endif +} + void zend_accel_shared_protect(bool protected) { +#ifdef ZEND_ACCEL_USE_PKEYS + if (zend_accel_pkey) { + if (pkey_set(zend_accel_pkey, protected ? PKEY_DISABLE_WRITE : 0) != 0) { + ZEND_UNREACHABLE(); + } + return; + } +#endif + #if defined(HAVE_MPROTECT) || defined(ZEND_WIN32) int i; diff --git a/ext/opcache/zend_shared_alloc.h b/ext/opcache/zend_shared_alloc.h index e9568fc368b7..667c13b94090 100644 --- a/ext/opcache/zend_shared_alloc.h +++ b/ext/opcache/zend_shared_alloc.h @@ -196,6 +196,8 @@ const char *zend_accel_get_shared_model(void); */ void zend_accel_shared_protect(bool protected); +extern int zend_accel_pkey; + #ifdef USE_MMAP extern const zend_shared_memory_handlers zend_alloc_mmap_handlers; #endif