1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix GH-19974: fpm_status_export_to_zval segfault for parallel execution

The fix fixes some other races that could result in mixed up copy and
nprocs number. It requires creating a copy in a similar way like for
request status.

This was not previously used to not impact other requests. However this
does not make that much sense as the only thing that impacts it is
holding the lock and not waiting for it. It is true that if there was a
big contention then the lock would be acquired more often but that can
be achieved by using fpm_get_status in loop so it should not make a
huge difference hopefully.

Closes GH-19974
This commit is contained in:
Jakub Zelenka
2025-10-09 18:58:10 +02:00
parent c5fa7696e6
commit 059f9f78e5
2 changed files with 46 additions and 59 deletions

4
NEWS
View File

@@ -7,6 +7,10 @@ PHP NEWS
. Fixed bug GH-20073 (Assertion failure in WeakMap offset operations on
reference). (nielsdos)
- FPM:
. Fixed bug GH-19974 (fpm_status_export_to_zval segfault for parallel
execution). (Jakub Zelenka, txuna)
- Random:
. Fix Randomizer::__serialize() w.r.t. INDIRECTs. (nielsdos)

View File

@@ -47,92 +47,75 @@ int fpm_status_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
int fpm_status_export_to_zval(zval *status)
{
struct fpm_scoreboard_s scoreboard, *scoreboard_p;
struct fpm_scoreboard_s *scoreboard_p;
struct fpm_scoreboard_proc_s *proc_p;
zval fpm_proc_stats, fpm_proc_stat;
time_t now_epoch;
struct timeval duration, now;
double cpu;
int i;
scoreboard_p = fpm_scoreboard_acquire(NULL, 1);
if (!scoreboard_p) {
zlog(ZLOG_NOTICE, "[pool (unknown)] status: scoreboard already in use.");
return -1;
}
/* copy the scoreboard not to bother other processes */
scoreboard = *scoreboard_p;
struct fpm_scoreboard_proc_s *procs = safe_emalloc(
sizeof(struct fpm_scoreboard_proc_s), scoreboard.nprocs, 0);
struct fpm_scoreboard_proc_s *proc_p;
for(i=0; i<scoreboard.nprocs; i++) {
proc_p = fpm_scoreboard_proc_acquire(scoreboard_p, i, 1);
if (!proc_p){
procs[i].used=-1;
continue;
}
procs[i] = *proc_p;
fpm_scoreboard_proc_release(proc_p);
}
fpm_scoreboard_release(scoreboard_p);
scoreboard_p = fpm_scoreboard_copy(NULL, 1);
now_epoch = time(NULL);
fpm_clock_get(&now);
array_init(status);
add_assoc_string(status, "pool", scoreboard.pool);
add_assoc_string(status, "process-manager", PM2STR(scoreboard.pm));
add_assoc_long(status, "start-time", scoreboard.start_epoch);
add_assoc_long(status, "start-since", now_epoch - scoreboard.start_epoch);
add_assoc_long(status, "accepted-conn", scoreboard.requests);
add_assoc_long(status, "listen-queue", scoreboard.lq);
add_assoc_long(status, "max-listen-queue", scoreboard.lq_max);
add_assoc_long(status, "listen-queue-len", scoreboard.lq_len);
add_assoc_long(status, "idle-processes", scoreboard.idle);
add_assoc_long(status, "active-processes", scoreboard.active);
add_assoc_long(status, "total-processes", scoreboard.idle + scoreboard.active);
add_assoc_long(status, "max-active-processes", scoreboard.active_max);
add_assoc_long(status, "max-children-reached", scoreboard.max_children_reached);
add_assoc_long(status, "slow-requests", scoreboard.slow_rq);
add_assoc_string(status, "pool", scoreboard_p->pool);
add_assoc_string(status, "process-manager", PM2STR(scoreboard_p->pm));
add_assoc_long(status, "start-time", scoreboard_p->start_epoch);
add_assoc_long(status, "start-since", now_epoch - scoreboard_p->start_epoch);
add_assoc_long(status, "accepted-conn", scoreboard_p->requests);
add_assoc_long(status, "listen-queue", scoreboard_p->lq);
add_assoc_long(status, "max-listen-queue", scoreboard_p->lq_max);
add_assoc_long(status, "listen-queue-len", scoreboard_p->lq_len);
add_assoc_long(status, "idle-processes", scoreboard_p->idle);
add_assoc_long(status, "active-processes", scoreboard_p->active);
add_assoc_long(status, "total-processes", scoreboard_p->idle + scoreboard_p->active);
add_assoc_long(status, "max-active-processes", scoreboard_p->active_max);
add_assoc_long(status, "max-children-reached", scoreboard_p->max_children_reached);
add_assoc_long(status, "slow-requests", scoreboard_p->slow_rq);
array_init(&fpm_proc_stats);
for(i=0; i<scoreboard.nprocs; i++) {
if (!procs[i].used) {
for(i = 0; i < scoreboard_p->nprocs; i++) {
proc_p = &scoreboard_p->procs[i];
if (!proc_p->used) {
continue;
}
proc_p = &procs[i];
/* prevent NaN */
if (procs[i].cpu_duration.tv_sec == 0 && procs[i].cpu_duration.tv_usec == 0) {
if (proc_p->cpu_duration.tv_sec == 0 && proc_p->cpu_duration.tv_usec == 0) {
cpu = 0.;
} else {
cpu = (procs[i].last_request_cpu.tms_utime + procs[i].last_request_cpu.tms_stime + procs[i].last_request_cpu.tms_cutime + procs[i].last_request_cpu.tms_cstime) / fpm_scoreboard_get_tick() / (procs[i].cpu_duration.tv_sec + procs[i].cpu_duration.tv_usec / 1000000.) * 100.;
cpu = (proc_p->last_request_cpu.tms_utime + proc_p->last_request_cpu.tms_stime + proc_p->last_request_cpu.tms_cutime +
proc_p->last_request_cpu.tms_cstime) / fpm_scoreboard_get_tick() /
(proc_p->cpu_duration.tv_sec + proc_p->cpu_duration.tv_usec / 1000000.) * 100.;
}
array_init(&fpm_proc_stat);
add_assoc_long(&fpm_proc_stat, "pid", procs[i].pid);
add_assoc_string(&fpm_proc_stat, "state", fpm_request_get_stage_name(procs[i].request_stage));
add_assoc_long(&fpm_proc_stat, "start-time", procs[i].start_epoch);
add_assoc_long(&fpm_proc_stat, "start-since", now_epoch - procs[i].start_epoch);
add_assoc_long(&fpm_proc_stat, "requests", procs[i].requests);
if (procs[i].request_stage == FPM_REQUEST_ACCEPTING) {
duration = procs[i].duration;
add_assoc_long(&fpm_proc_stat, "pid", proc_p->pid);
add_assoc_string(&fpm_proc_stat, "state", fpm_request_get_stage_name(proc_p->request_stage));
add_assoc_long(&fpm_proc_stat, "start-time", proc_p->start_epoch);
add_assoc_long(&fpm_proc_stat, "start-since", now_epoch - proc_p->start_epoch);
add_assoc_long(&fpm_proc_stat, "requests", proc_p->requests);
if (proc_p->request_stage == FPM_REQUEST_ACCEPTING) {
duration = proc_p->duration;
} else {
timersub(&now, &procs[i].accepted, &duration);
timersub(&now, &proc_p->accepted, &duration);
}
add_assoc_long(&fpm_proc_stat, "request-duration", duration.tv_sec * 1000000UL + duration.tv_usec);
add_assoc_string(&fpm_proc_stat, "request-method", procs[i].request_method[0] != '\0' ? procs[i].request_method : "-");
add_assoc_string(&fpm_proc_stat, "request-uri", procs[i].request_uri);
add_assoc_string(&fpm_proc_stat, "query-string", procs[i].query_string);
add_assoc_long(&fpm_proc_stat, "request-length", procs[i].content_length);
add_assoc_string(&fpm_proc_stat, "user", procs[i].auth_user[0] != '\0' ? procs[i].auth_user : "-");
add_assoc_string(&fpm_proc_stat, "script", procs[i].script_filename[0] != '\0' ? procs[i].script_filename : "-");
add_assoc_double(&fpm_proc_stat, "last-request-cpu", procs[i].request_stage == FPM_REQUEST_ACCEPTING ? cpu : 0.);
add_assoc_long(&fpm_proc_stat, "last-request-memory", procs[i].request_stage == FPM_REQUEST_ACCEPTING ? procs[i].memory : 0);
add_assoc_string(&fpm_proc_stat, "request-method", proc_p->request_method[0] != '\0' ? proc_p->request_method : "-");
add_assoc_string(&fpm_proc_stat, "request-uri", proc_p->request_uri);
add_assoc_string(&fpm_proc_stat, "query-string", proc_p->query_string);
add_assoc_long(&fpm_proc_stat, "request-length", proc_p->content_length);
add_assoc_string(&fpm_proc_stat, "user", proc_p->auth_user[0] != '\0' ? proc_p->auth_user : "-");
add_assoc_string(&fpm_proc_stat, "script", proc_p->script_filename[0] != '\0' ? proc_p->script_filename : "-");
add_assoc_double(&fpm_proc_stat, "last-request-cpu", proc_p->request_stage == FPM_REQUEST_ACCEPTING ? cpu : 0.);
add_assoc_long(&fpm_proc_stat, "last-request-memory", proc_p->request_stage == FPM_REQUEST_ACCEPTING ? proc_p->memory : 0);
add_next_index_zval(&fpm_proc_stats, &fpm_proc_stat);
}
add_assoc_zval(status, "procs", &fpm_proc_stats);
efree(procs);
fpm_scoreboard_free_copy(scoreboard_p);
return 0;
}