From 73dabdfebec739289ce83e5294bd926f3d426349 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Fri, 9 Sep 2022 15:04:09 +0200 Subject: [PATCH] Refactor accel_finish_startup --- ext/opcache/ZendAccelerator.c | 405 ++++++++++++++++++---------------- 1 file changed, 213 insertions(+), 192 deletions(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index acfb3d24f64..2c3dd299f16 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -4572,206 +4572,227 @@ static void preload_send_header(sapi_header_struct *sapi_header, void *server_co { } +#ifndef ZEND_WIN32 +static int accel_finish_startup_preload(bool in_child) +{ + int ret = SUCCESS; + int rc; + int orig_error_reporting; + + int (*orig_activate)(void) = sapi_module.activate; + int (*orig_deactivate)(void) = sapi_module.deactivate; + void (*orig_register_server_variables)(zval *track_vars_array) = sapi_module.register_server_variables; + int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers) = sapi_module.header_handler; + int (*orig_send_headers)(sapi_headers_struct *sapi_headers) = sapi_module.send_headers; + void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context)= sapi_module.send_header; + char *(*orig_getenv)(const char *name, size_t name_len) = sapi_module.getenv; + size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write; + void (*orig_flush)(void *server_context) = sapi_module.flush; +#ifdef ZEND_SIGNALS + bool old_reset_signals = SIGG(reset); +#endif + + sapi_module.activate = NULL; + sapi_module.deactivate = NULL; + sapi_module.register_server_variables = NULL; + sapi_module.header_handler = preload_header_handler; + sapi_module.send_headers = preload_send_headers; + sapi_module.send_header = preload_send_header; + sapi_module.getenv = NULL; + sapi_module.ub_write = preload_ub_write; + sapi_module.flush = preload_flush; + + zend_interned_strings_switch_storage(1); + +#ifdef ZEND_SIGNALS + SIGG(reset) = false; +#endif + + orig_error_reporting = EG(error_reporting); + EG(error_reporting) = 0; + + rc = php_request_startup(); + + EG(error_reporting) = orig_error_reporting; + + if (rc == SUCCESS) { + bool orig_report_memleaks; + + /* don't send headers */ + SG(headers_sent) = true; + SG(request_info).no_headers = true; + php_output_set_status(0); + + ZCG(auto_globals_mask) = 0; + ZCG(request_time) = (time_t)sapi_get_request_time(); + ZCG(cache_opline) = NULL; + ZCG(cache_persistent_script) = NULL; + ZCG(include_path_key_len) = 0; + ZCG(include_path_check) = true; + + ZCG(cwd) = NULL; + ZCG(cwd_key_len) = 0; + ZCG(cwd_check) = true; + + if (accel_preload(ZCG(accel_directives).preload, in_child) != SUCCESS) { + ret = FAILURE; + } + preload_flush(NULL); + + orig_report_memleaks = PG(report_memleaks); + PG(report_memleaks) = false; +#ifdef ZEND_SIGNALS + /* We may not have registered signal handlers due to SIGG(reset)=0, so + * also disable the check that they are registered. */ + SIGG(check) = false; +#endif + php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */ + PG(report_memleaks) = orig_report_memleaks; + } else { + zend_shared_alloc_unlock(); + ret = FAILURE; + } +#ifdef ZEND_SIGNALS + SIGG(reset) = old_reset_signals; +#endif + + sapi_module.activate = orig_activate; + sapi_module.deactivate = orig_deactivate; + sapi_module.register_server_variables = orig_register_server_variables; + sapi_module.header_handler = orig_header_handler; + sapi_module.send_headers = orig_send_headers; + sapi_module.send_header = orig_send_header; + sapi_module.getenv = orig_getenv; + sapi_module.ub_write = orig_ub_write; + sapi_module.flush = orig_flush; + + sapi_activate(); + + return ret; +} + +static int accel_finish_startup_preload_subprocess(pid_t *pid) +{ + uid_t euid = geteuid(); + if (euid != 0) { + if (ZCG(accel_directives).preload_user + && *ZCG(accel_directives).preload_user) { + zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored"); + } + + *pid = -1; + return SUCCESS; + } + + if (!ZCG(accel_directives).preload_user + || !*ZCG(accel_directives).preload_user) { + + + zend_shared_alloc_unlock(); + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload\" requires \"opcache.preload_user\" when running under uid 0"); + return FAILURE; + } + + struct passwd *pw = getpwnam(ZCG(accel_directives).preload_user); + if (pw == NULL) { + zend_shared_alloc_unlock(); + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user); + return FAILURE; + } + + if (pw->pw_uid == euid) { + *pid = -1; + return SUCCESS; + } + + *pid = fork(); + if (*pid == -1) { + zend_shared_alloc_unlock(); + zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to fork()"); + return FAILURE; + } + + if (*pid == 0) { /* children */ + if (setgid(pw->pw_gid) < 0) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setgid(%d)", pw->pw_gid); + exit(1); + } + if (initgroups(pw->pw_name, pw->pw_gid) < 0) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to initgroups(\"%s\", %d)", pw->pw_name, pw->pw_uid); + exit(1); + } + if (setuid(pw->pw_uid) < 0) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setuid(%d)", pw->pw_uid); + exit(1); + } + } + + return SUCCESS; +} +#endif /* ZEND_WIN32 */ + static int accel_finish_startup(void) { if (!ZCG(enabled) || !accel_startup_ok) { return SUCCESS; } - if (ZCG(accel_directives).preload && *ZCG(accel_directives).preload) { -#ifdef ZEND_WIN32 - zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Preloading is not supported on Windows"); - return FAILURE; -#else - bool in_child = false; - int ret = SUCCESS; - int rc; - int orig_error_reporting; - - int (*orig_activate)(void) = sapi_module.activate; - int (*orig_deactivate)(void) = sapi_module.deactivate; - void (*orig_register_server_variables)(zval *track_vars_array) = sapi_module.register_server_variables; - int (*orig_header_handler)(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers) = sapi_module.header_handler; - int (*orig_send_headers)(sapi_headers_struct *sapi_headers) = sapi_module.send_headers; - void (*orig_send_header)(sapi_header_struct *sapi_header, void *server_context)= sapi_module.send_header; - char *(*orig_getenv)(const char *name, size_t name_len) = sapi_module.getenv; - size_t (*orig_ub_write)(const char *str, size_t str_length) = sapi_module.ub_write; - void (*orig_flush)(void *server_context) = sapi_module.flush; -#ifdef ZEND_SIGNALS - bool old_reset_signals = SIGG(reset); -#endif - - if (UNEXPECTED(file_cache_only)) { - zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode"); - return SUCCESS; - } - - /* exclusive lock */ - zend_shared_alloc_lock(); - - if (ZCSG(preload_script)) { - /* Preloading was done in another process */ - preload_load(); - zend_shared_alloc_unlock(); - return SUCCESS; - } - - uid_t euid = geteuid(); - if (euid == 0) { - pid_t pid; - struct passwd *pw; - - if (!ZCG(accel_directives).preload_user - || !*ZCG(accel_directives).preload_user) { - zend_shared_alloc_unlock(); - zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload\" requires \"opcache.preload_user\" when running under uid 0"); - return FAILURE; - } - - pw = getpwnam(ZCG(accel_directives).preload_user); - if (pw == NULL) { - zend_shared_alloc_unlock(); - zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to getpwnam(\"%s\")", ZCG(accel_directives).preload_user); - return FAILURE; - } - - if (pw->pw_uid != euid) { - pid = fork(); - if (pid == -1) { - zend_shared_alloc_unlock(); - zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to fork()"); - return FAILURE; - } else if (pid == 0) { /* children */ - if (setgid(pw->pw_gid) < 0) { - zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setgid(%d)", pw->pw_gid); - exit(1); - } - if (initgroups(pw->pw_name, pw->pw_gid) < 0) { - zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to initgroups(\"%s\", %d)", pw->pw_name, pw->pw_uid); - exit(1); - } - if (setuid(pw->pw_uid) < 0) { - zend_accel_error(ACCEL_LOG_WARNING, "Preloading failed to setuid(%d)", pw->pw_uid); - exit(1); - } - in_child = true; - } else { /* parent */ - 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); - return FAILURE; - } - - if (ZCSG(preload_script)) { - preload_load(); - } - - zend_shared_alloc_unlock(); - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { - return SUCCESS; - } else { - return FAILURE; - } - } - } - } else { - if (ZCG(accel_directives).preload_user - && *ZCG(accel_directives).preload_user) { - zend_accel_error(ACCEL_LOG_WARNING, "\"opcache.preload_user\" is ignored"); - } - } - - sapi_module.activate = NULL; - sapi_module.deactivate = NULL; - sapi_module.register_server_variables = NULL; - sapi_module.header_handler = preload_header_handler; - sapi_module.send_headers = preload_send_headers; - sapi_module.send_header = preload_send_header; - sapi_module.getenv = NULL; - sapi_module.ub_write = preload_ub_write; - sapi_module.flush = preload_flush; - - zend_interned_strings_switch_storage(1); - -#ifdef ZEND_SIGNALS - SIGG(reset) = false; -#endif - - orig_error_reporting = EG(error_reporting); - EG(error_reporting) = 0; - - rc = php_request_startup(); - - EG(error_reporting) = orig_error_reporting; - - if (rc == SUCCESS) { - bool orig_report_memleaks; - - /* don't send headers */ - SG(headers_sent) = true; - SG(request_info).no_headers = true; - php_output_set_status(0); - - ZCG(auto_globals_mask) = 0; - ZCG(request_time) = (time_t)sapi_get_request_time(); - ZCG(cache_opline) = NULL; - ZCG(cache_persistent_script) = NULL; - ZCG(include_path_key_len) = 0; - ZCG(include_path_check) = true; - - ZCG(cwd) = NULL; - ZCG(cwd_key_len) = 0; - ZCG(cwd_check) = true; - - if (accel_preload(ZCG(accel_directives).preload, in_child) != SUCCESS) { - ret = FAILURE; - } - preload_flush(NULL); - - orig_report_memleaks = PG(report_memleaks); - PG(report_memleaks) = false; -#ifdef ZEND_SIGNALS - /* We may not have registered signal handlers due to SIGG(reset)=0, so - * also disable the check that they are registered. */ - SIGG(check) = false; -#endif - php_request_shutdown(NULL); /* calls zend_shared_alloc_unlock(); */ - PG(report_memleaks) = orig_report_memleaks; - } else { - zend_shared_alloc_unlock(); - ret = FAILURE; - } -#ifdef ZEND_SIGNALS - SIGG(reset) = old_reset_signals; -#endif - - sapi_module.activate = orig_activate; - sapi_module.deactivate = orig_deactivate; - sapi_module.register_server_variables = orig_register_server_variables; - sapi_module.header_handler = orig_header_handler; - sapi_module.send_headers = orig_send_headers; - sapi_module.send_header = orig_send_header; - sapi_module.getenv = orig_getenv; - sapi_module.ub_write = orig_ub_write; - sapi_module.flush = orig_flush; - - sapi_activate(); - - if (in_child) { - if (ret == SUCCESS) { - exit(0); - } else { - exit(2); - } - } - - return ret; -#endif + if (!(ZCG(accel_directives).preload && *ZCG(accel_directives).preload)) { + return SUCCESS; } - return SUCCESS; +#ifdef ZEND_WIN32 + zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Preloading is not supported on Windows"); + return FAILURE; +#else /* ZEND_WIN32 */ + + if (UNEXPECTED(file_cache_only)) { + zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode"); + return SUCCESS; + } + + /* exclusive lock */ + zend_shared_alloc_lock(); + + if (ZCSG(preload_script)) { + /* Preloading was done in another process */ + preload_load(); + zend_shared_alloc_unlock(); + return SUCCESS; + } + + + pid_t pid; + if (accel_finish_startup_preload_subprocess(&pid) == FAILURE) { + zend_shared_alloc_unlock(); + return FAILURE; + } + + if (pid == -1) { /* no subprocess was needed */ + return accel_finish_startup_preload(false); + } else if (pid == 0) { /* subprocess */ + int ret = accel_finish_startup_preload(true); + + exit(ret == SUCCESS ? 0 : 1); + } else { /* parent */ + 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); + } + + if (ZCSG(preload_script)) { + preload_load(); + } + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + return SUCCESS; + } else { + return FAILURE; + } + } +#endif /* ZEND_WIN32 */ } ZEND_EXT_API zend_extension zend_extension_entry = {