diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 9ef74973a4df..e6a2b90e8fff 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -4524,16 +4524,6 @@ static void preload_load(size_t orig_map_ptr_static_last) } } - if (EG(zend_constants)) { - EG(persistent_constants_count) = EG(zend_constants)->nNumUsed; - } - if (EG(function_table)) { - EG(persistent_functions_count) = EG(function_table)->nNumUsed; - } - if (EG(class_table)) { - EG(persistent_classes_count) = EG(class_table)->nNumUsed; - } - size_t old_map_ptr_last = CG(map_ptr_last); if (zend_map_ptr_static_last != ZCSG(map_ptr_static_last) || old_map_ptr_last != ZCSG(map_ptr_last)) { CG(map_ptr_last) = ZCSG(map_ptr_last); @@ -4803,6 +4793,12 @@ static zend_result accel_preload(const char *config, bool in_child) preload_load(orig_map_ptr_static_last); + /* Update persistent counts, as shutdown will discard anything past + * that, and these tables are aliases to global ones at this point. */ + EG(persistent_functions_count) = EG(function_table)->nNumUsed; + EG(persistent_classes_count) = EG(class_table)->nNumUsed; + EG(persistent_constants_count) = EG(zend_constants)->nNumUsed; + /* Store individual scripts with unlinked classes */ HANDLE_BLOCK_INTERRUPTIONS(); SHM_UNPROTECT(); @@ -5094,13 +5090,45 @@ static zend_result accel_finish_startup(void) exit(ret == SUCCESS ? 0 : 1); } else { /* parent */ - int status; +# ifdef HAVE_SIGPROCMASK + /* Interrupting the waitpid() call below with a signal would cause the + * process to exit. This is fine when the signal disposition is set to + * terminate the process, but not otherwise. + * When running the apache2handler, preloading is performed in the + * control process. SIGUSR1 and SIGHUP are used to tell the control + * process to restart children. Exiting when these signals are received + * would unexpectedly shutdown the server instead of restarting it. + * Block the USR1 and HUP signals from being delivered during the + * syscall when running the apache2handler SAPI, as these are not + * supposed to terminate the process. See GH-20051. */ + bool is_apache2handler = strcmp(sapi_module.name, "apache2handler") == 0; + sigset_t set, oldset; + if (is_apache2handler) { + if (sigemptyset(&set) + || sigaddset(&set, SIGUSR1) + || sigaddset(&set, SIGHUP)) { + ZEND_UNREACHABLE(); + } + if (sigprocmask(SIG_BLOCK, &set, &oldset)) { + ZEND_UNREACHABLE(); + } + } +# endif + int status; if (waitpid(pid, &status, 0) < 0) { zend_shared_alloc_unlock(); zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid); } +# ifdef HAVE_SIGPROCMASK + if (is_apache2handler) { + if (sigprocmask(SIG_SETMASK, &oldset, NULL)) { + ZEND_UNREACHABLE(); + } + } +# endif + if (ZCSG(preload_script)) { preload_load(zend_map_ptr_static_last); }