Skip to content

Do not require opcache.preload_user in cli SAPIs #9515

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
358 changes: 193 additions & 165 deletions ext/opcache/ZendAccelerator.c
Original file line number Diff line number Diff line change
Expand Up @@ -4565,206 +4565,234 @@ static void preload_send_header(sapi_header_struct *sapi_header, void *server_co
{
}

static int accel_finish_startup(void)
#ifndef ZEND_WIN32
static int accel_finish_startup_preload(bool in_child)
{
if (!ZCG(enabled) || !accel_startup_ok) {
return SUCCESS;
}
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);

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);
SIGG(reset) = false;
#endif

if (UNEXPECTED(file_cache_only)) {
zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode");
return SUCCESS;
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);

/* exclusive lock */
zend_shared_alloc_lock();
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

if (ZCSG(preload_script)) {
/* Preloading was done in another process */
preload_load();
zend_shared_alloc_unlock();
return SUCCESS;
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 because the current user is not \"root\"");
}

uid_t euid = geteuid();
if (euid == 0) {
pid_t pid;
struct passwd *pw;
*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;
}
if (!ZCG(accel_directives).preload_user
|| !*ZCG(accel_directives).preload_user) {

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;
}
bool sapi_requires_preload_user = !(strcmp(sapi_module.name, "cli") == 0
|| strcmp(sapi_module.name, "phpdbg") == 0);

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 (!sapi_requires_preload_user) {
*pid = -1;
return SUCCESS;
}

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;
}
zend_shared_alloc_unlock();
zend_accel_error_noreturn(ACCEL_LOG_FATAL, "\"opcache.preload\" requires \"opcache.preload_user\" when running under uid 0");
return FAILURE;
}

if (ZCSG(preload_script)) {
preload_load();
}
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;
}

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");
}
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);
}
}

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;
return SUCCESS;
}
#endif /* ZEND_WIN32 */

zend_interned_strings_switch_storage(1);
static int accel_finish_startup(void)
{
if (!ZCG(enabled) || !accel_startup_ok) {
return SUCCESS;
}

#ifdef ZEND_SIGNALS
SIGG(reset) = false;
#endif
if (!(ZCG(accel_directives).preload && *ZCG(accel_directives).preload)) {
return SUCCESS;
}

orig_error_reporting = EG(error_reporting);
EG(error_reporting) = 0;
#ifdef ZEND_WIN32
zend_accel_error_noreturn(ACCEL_LOG_ERROR, "Preloading is not supported on Windows");
return FAILURE;
#else /* ZEND_WIN32 */

rc = php_request_startup();
if (UNEXPECTED(file_cache_only)) {
zend_accel_error(ACCEL_LOG_WARNING, "Preloading doesn't work in \"file_cache_only\" mode");
return SUCCESS;
}

EG(error_reporting) = orig_error_reporting;
/* exclusive lock */
zend_shared_alloc_lock();

if (rc == SUCCESS) {
bool orig_report_memleaks;
if (ZCSG(preload_script)) {
/* Preloading was done in another process */
preload_load();
zend_shared_alloc_unlock();
return SUCCESS;
}

/* 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;
pid_t pid;
if (accel_finish_startup_preload_subprocess(&pid) == FAILURE) {
zend_shared_alloc_unlock();
return FAILURE;
}

ZCG(cwd) = NULL;
ZCG(cwd_key_len) = 0;
ZCG(cwd_check) = true;
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);

if (accel_preload(ZCG(accel_directives).preload, in_child) != SUCCESS) {
ret = FAILURE;
}
preload_flush(NULL);
exit(ret == SUCCESS ? 0 : 1);
} else { /* parent */
int status;

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 {
if (waitpid(pid, &status, 0) < 0) {
zend_shared_alloc_unlock();
ret = FAILURE;
zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Preloading failed to waitpid(%d)", pid);
}
#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);
}
if (ZCSG(preload_script)) {
preload_load();
}

return ret;
#endif
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
return SUCCESS;
} else {
return FAILURE;
}
}

return SUCCESS;
#endif /* ZEND_WIN32 */
}

ZEND_EXT_API zend_extension zend_extension_entry = {
Expand Down
Loading