Skip to content

Commit fadb1f8

Browse files
bukkasmalyshev
authored andcommitted
Fix bug #81026 (PHP-FPM oob R/W in root process leading to priv escalation)
The main change is to store scoreboard procs directly to the variable sized array rather than indirectly through the pointer. Signed-off-by: Stanislav Malyshev <stas@php.net>
1 parent 6bd5271 commit fadb1f8

File tree

6 files changed

+81
-60
lines changed

6 files changed

+81
-60
lines changed

sapi/fpm/fpm/fpm_children.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ void fpm_children_bury() /* {{{ */
246246

247247
fpm_child_unlink(child);
248248

249-
fpm_scoreboard_proc_free(wp->scoreboard, child->scoreboard_i);
249+
fpm_scoreboard_proc_free(child);
250250

251251
fpm_clock_get(&tv1);
252252

@@ -256,9 +256,9 @@ void fpm_children_bury() /* {{{ */
256256
if (!fpm_pctl_can_spawn_children()) {
257257
severity = ZLOG_DEBUG;
258258
}
259-
zlog(severity, "[pool %s] child %d exited %s after %ld.%06d seconds from start", child->wp->config->name, (int) pid, buf, tv2.tv_sec, (int) tv2.tv_usec);
259+
zlog(severity, "[pool %s] child %d exited %s after %ld.%06d seconds from start", wp->config->name, (int) pid, buf, tv2.tv_sec, (int) tv2.tv_usec);
260260
} else {
261-
zlog(ZLOG_DEBUG, "[pool %s] child %d has been killed by the process management after %ld.%06d seconds from start", child->wp->config->name, (int) pid, tv2.tv_sec, (int) tv2.tv_usec);
261+
zlog(ZLOG_DEBUG, "[pool %s] child %d has been killed by the process management after %ld.%06d seconds from start", wp->config->name, (int) pid, tv2.tv_sec, (int) tv2.tv_usec);
262262
}
263263

264264
fpm_child_close(child, 1 /* in event_loop */);
@@ -324,7 +324,7 @@ static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp) /
324324
return 0;
325325
}
326326

327-
if (0 > fpm_scoreboard_proc_alloc(wp->scoreboard, &c->scoreboard_i)) {
327+
if (0 > fpm_scoreboard_proc_alloc(c)) {
328328
fpm_stdio_discard_pipes(c);
329329
fpm_child_free(c);
330330
return 0;
@@ -336,7 +336,7 @@ static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp) /
336336

337337
static void fpm_resources_discard(struct fpm_child_s *child) /* {{{ */
338338
{
339-
fpm_scoreboard_proc_free(child->wp->scoreboard, child->scoreboard_i);
339+
fpm_scoreboard_proc_free(child);
340340
fpm_stdio_discard_pipes(child);
341341
fpm_child_free(child);
342342
}
@@ -349,10 +349,10 @@ static void fpm_child_resources_use(struct fpm_child_s *child) /* {{{ */
349349
if (wp == child->wp || wp == child->wp->shared) {
350350
continue;
351351
}
352-
fpm_scoreboard_free(wp->scoreboard);
352+
fpm_scoreboard_free(wp);
353353
}
354354

355-
fpm_scoreboard_child_use(child->wp->scoreboard, child->scoreboard_i, getpid());
355+
fpm_scoreboard_child_use(child, getpid());
356356
fpm_stdio_child_use_pipes(child);
357357
fpm_child_free(child);
358358
}

sapi/fpm/fpm/fpm_request.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ int fpm_request_is_idle(struct fpm_child_s *child) /* {{{ */
285285
struct fpm_scoreboard_proc_s *proc;
286286

287287
/* no need in atomicity here */
288-
proc = fpm_scoreboard_proc_get(child->wp->scoreboard, child->scoreboard_i);
288+
proc = fpm_scoreboard_proc_get_from_child(child);
289289
if (!proc) {
290290
return 0;
291291
}
@@ -300,7 +300,7 @@ int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv) /*
300300

301301
if (!tv) return -1;
302302

303-
proc = fpm_scoreboard_proc_get(child->wp->scoreboard, child->scoreboard_i);
303+
proc = fpm_scoreboard_proc_get_from_child(child);
304304
if (!proc) {
305305
return -1;
306306
}

sapi/fpm/fpm/fpm_scoreboard.c

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <time.h>
77

88
#include "fpm_config.h"
9+
#include "fpm_children.h"
910
#include "fpm_scoreboard.h"
1011
#include "fpm_shm.h"
1112
#include "fpm_sockets.h"
@@ -23,7 +24,6 @@ static float fpm_scoreboard_tick;
2324
int fpm_scoreboard_init_main() /* {{{ */
2425
{
2526
struct fpm_worker_pool_s *wp;
26-
unsigned int i;
2727

2828
#ifdef HAVE_TIMES
2929
#if (defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK))
@@ -40,7 +40,7 @@ int fpm_scoreboard_init_main() /* {{{ */
4040

4141

4242
for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
43-
size_t scoreboard_size, scoreboard_nprocs_size;
43+
size_t scoreboard_procs_size;
4444
void *shm_mem;
4545

4646
if (wp->config->pm_max_children < 1) {
@@ -53,22 +53,15 @@ int fpm_scoreboard_init_main() /* {{{ */
5353
return -1;
5454
}
5555

56-
scoreboard_size = sizeof(struct fpm_scoreboard_s) + (wp->config->pm_max_children) * sizeof(struct fpm_scoreboard_proc_s *);
57-
scoreboard_nprocs_size = sizeof(struct fpm_scoreboard_proc_s) * wp->config->pm_max_children;
58-
shm_mem = fpm_shm_alloc(scoreboard_size + scoreboard_nprocs_size);
56+
scoreboard_procs_size = sizeof(struct fpm_scoreboard_proc_s) * wp->config->pm_max_children;
57+
shm_mem = fpm_shm_alloc(sizeof(struct fpm_scoreboard_s) + scoreboard_procs_size);
5958

6059
if (!shm_mem) {
6160
return -1;
6261
}
63-
wp->scoreboard = shm_mem;
62+
wp->scoreboard = shm_mem;
63+
wp->scoreboard->pm = wp->config->pm;
6464
wp->scoreboard->nprocs = wp->config->pm_max_children;
65-
shm_mem += scoreboard_size;
66-
67-
for (i = 0; i < wp->scoreboard->nprocs; i++, shm_mem += sizeof(struct fpm_scoreboard_proc_s)) {
68-
wp->scoreboard->procs[i] = shm_mem;
69-
}
70-
71-
wp->scoreboard->pm = wp->config->pm;
7265
wp->scoreboard->start_epoch = time(NULL);
7366
strlcpy(wp->scoreboard->pool, wp->config->name, sizeof(wp->scoreboard->pool));
7467

@@ -167,28 +160,48 @@ struct fpm_scoreboard_s *fpm_scoreboard_get() /* {{{*/
167160
}
168161
/* }}} */
169162

170-
struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{*/
163+
static inline struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get_ex(
164+
struct fpm_scoreboard_s *scoreboard, int child_index, unsigned int nprocs) /* {{{*/
171165
{
172166
if (!scoreboard) {
173-
scoreboard = fpm_scoreboard;
167+
return NULL;
174168
}
175169

176-
if (!scoreboard) {
170+
if (child_index < 0 || (unsigned int)child_index >= nprocs) {
177171
return NULL;
178172
}
179173

174+
return &scoreboard->procs[child_index];
175+
}
176+
/* }}} */
177+
178+
struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(
179+
struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{*/
180+
{
181+
if (!scoreboard) {
182+
scoreboard = fpm_scoreboard;
183+
}
184+
180185
if (child_index < 0) {
181186
child_index = fpm_scoreboard_i;
182187
}
183188

184-
if (child_index < 0 || (unsigned int)child_index >= scoreboard->nprocs) {
185-
return NULL;
186-
}
189+
return fpm_scoreboard_proc_get_ex(scoreboard, child_index, scoreboard->nprocs);
190+
}
191+
/* }}} */
187192

188-
return scoreboard->procs[child_index];
193+
struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get_from_child(struct fpm_child_s *child) /* {{{*/
194+
{
195+
struct fpm_worker_pool_s *wp = child->wp;
196+
unsigned int nprocs = wp->config->pm_max_children;
197+
struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
198+
int child_index = child->scoreboard_i;
199+
200+
return fpm_scoreboard_proc_get_ex(scoreboard, child_index, nprocs);
189201
}
190202
/* }}} */
191203

204+
192205
struct fpm_scoreboard_s *fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang) /* {{{ */
193206
{
194207
struct fpm_scoreboard_s *s;
@@ -239,28 +252,28 @@ void fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s *proc) /* {{{ */
239252
proc->lock = 0;
240253
}
241254

242-
void fpm_scoreboard_free(struct fpm_scoreboard_s *scoreboard) /* {{{ */
255+
void fpm_scoreboard_free(struct fpm_worker_pool_s *wp) /* {{{ */
243256
{
244-
size_t scoreboard_size, scoreboard_nprocs_size;
257+
size_t scoreboard_procs_size;
258+
struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
245259

246260
if (!scoreboard) {
247261
zlog(ZLOG_ERROR, "**scoreboard is NULL");
248262
return;
249263
}
250264

251-
scoreboard_size = sizeof(struct fpm_scoreboard_s) + (scoreboard->nprocs) * sizeof(struct fpm_scoreboard_proc_s *);
252-
scoreboard_nprocs_size = sizeof(struct fpm_scoreboard_proc_s) * scoreboard->nprocs;
265+
scoreboard_procs_size = sizeof(struct fpm_scoreboard_proc_s) * wp->config->pm_max_children;
253266

254-
fpm_shm_free(scoreboard, scoreboard_size + scoreboard_nprocs_size);
267+
fpm_shm_free(scoreboard, sizeof(struct fpm_scoreboard_s) + scoreboard_procs_size);
255268
}
256269
/* }}} */
257270

258-
void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_index, pid_t pid) /* {{{ */
271+
void fpm_scoreboard_child_use(struct fpm_child_s *child, pid_t pid) /* {{{ */
259272
{
260273
struct fpm_scoreboard_proc_s *proc;
261-
fpm_scoreboard = scoreboard;
262-
fpm_scoreboard_i = child_index;
263-
proc = fpm_scoreboard_proc_get(scoreboard, child_index);
274+
fpm_scoreboard = child->wp->scoreboard;
275+
fpm_scoreboard_i = child->scoreboard_i;
276+
proc = fpm_scoreboard_proc_get_from_child(child);
264277
if (!proc) {
265278
return;
266279
}
@@ -269,60 +282,67 @@ void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_ind
269282
}
270283
/* }}} */
271284

272-
void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{ */
285+
void fpm_scoreboard_proc_free(struct fpm_child_s *child) /* {{{ */
273286
{
287+
struct fpm_worker_pool_s *wp = child->wp;
288+
struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
289+
int child_index = child->scoreboard_i;
290+
274291
if (!scoreboard) {
275292
return;
276293
}
277294

278-
if (child_index < 0 || (unsigned int)child_index >= scoreboard->nprocs) {
295+
if (child_index < 0 || child_index >= wp->config->pm_max_children) {
279296
return;
280297
}
281298

282-
if (scoreboard->procs[child_index] && scoreboard->procs[child_index]->used > 0) {
283-
memset(scoreboard->procs[child_index], 0, sizeof(struct fpm_scoreboard_proc_s));
299+
if (scoreboard->procs[child_index].used > 0) {
300+
memset(&scoreboard->procs[child_index], 0, sizeof(struct fpm_scoreboard_proc_s));
284301
}
285302

286303
/* set this slot as free to avoid search on next alloc */
287304
scoreboard->free_proc = child_index;
288305
}
289306
/* }}} */
290307

291-
int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_index) /* {{{ */
308+
int fpm_scoreboard_proc_alloc(struct fpm_child_s *child) /* {{{ */
292309
{
293310
int i = -1;
311+
struct fpm_worker_pool_s *wp = child->wp;
312+
struct fpm_scoreboard_s *scoreboard = wp->scoreboard;
313+
int nprocs = wp->config->pm_max_children;
294314

295-
if (!scoreboard || !child_index) {
315+
if (!scoreboard) {
296316
return -1;
297317
}
298318

299319
/* first try the slot which is supposed to be free */
300-
if (scoreboard->free_proc >= 0 && (unsigned int)scoreboard->free_proc < scoreboard->nprocs) {
301-
if (scoreboard->procs[scoreboard->free_proc] && !scoreboard->procs[scoreboard->free_proc]->used) {
320+
if (scoreboard->free_proc >= 0 && scoreboard->free_proc < nprocs) {
321+
if (!scoreboard->procs[scoreboard->free_proc].used) {
302322
i = scoreboard->free_proc;
303323
}
304324
}
305325

306326
if (i < 0) { /* the supposed free slot is not, let's search for a free slot */
307327
zlog(ZLOG_DEBUG, "[pool %s] the proc->free_slot was not free. Let's search", scoreboard->pool);
308-
for (i = 0; i < (int)scoreboard->nprocs; i++) {
309-
if (scoreboard->procs[i] && !scoreboard->procs[i]->used) { /* found */
328+
for (i = 0; i < nprocs; i++) {
329+
if (!scoreboard->procs[i].used) { /* found */
310330
break;
311331
}
312332
}
313333
}
314334

315335
/* no free slot */
316-
if (i < 0 || i >= (int)scoreboard->nprocs) {
336+
if (i < 0 || i >= nprocs) {
317337
zlog(ZLOG_ERROR, "[pool %s] no free scoreboard slot", scoreboard->pool);
318338
return -1;
319339
}
320340

321-
scoreboard->procs[i]->used = 1;
322-
*child_index = i;
341+
scoreboard->procs[i].used = 1;
342+
child->scoreboard_i = i;
323343

324344
/* supposed next slot is free */
325-
if (i + 1 >= (int)scoreboard->nprocs) {
345+
if (i + 1 >= nprocs) {
326346
scoreboard->free_proc = 0;
327347
} else {
328348
scoreboard->free_proc = i + 1;

sapi/fpm/fpm/fpm_scoreboard.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ struct fpm_scoreboard_s {
6464
int free_proc;
6565
unsigned long int slow_rq;
6666
struct fpm_scoreboard_s *shared;
67-
struct fpm_scoreboard_proc_s *procs[];
67+
struct fpm_scoreboard_proc_s procs[];
6868
};
6969

7070
int fpm_scoreboard_init_main();
@@ -73,18 +73,19 @@ int fpm_scoreboard_init_child(struct fpm_worker_pool_s *wp);
7373
void fpm_scoreboard_update(int idle, int active, int lq, int lq_len, int requests, int max_children_reached, int slow_rq, int action, struct fpm_scoreboard_s *scoreboard);
7474
struct fpm_scoreboard_s *fpm_scoreboard_get();
7575
struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(struct fpm_scoreboard_s *scoreboard, int child_index);
76+
struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get_from_child(struct fpm_child_s *child);
7677

7778
struct fpm_scoreboard_s *fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang);
7879
void fpm_scoreboard_release(struct fpm_scoreboard_s *scoreboard);
7980
struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_acquire(struct fpm_scoreboard_s *scoreboard, int child_index, int nohang);
8081
void fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s *proc);
8182

82-
void fpm_scoreboard_free(struct fpm_scoreboard_s *scoreboard);
83+
void fpm_scoreboard_free(struct fpm_worker_pool_s *wp);
8384

84-
void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_index, pid_t pid);
85+
void fpm_scoreboard_child_use(struct fpm_child_s *child, pid_t pid);
8586

86-
void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_index);
87-
int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_index);
87+
void fpm_scoreboard_proc_free(struct fpm_child_s *child);
88+
int fpm_scoreboard_proc_alloc(struct fpm_child_s *child);
8889

8990
#ifdef HAVE_TIMES
9091
float fpm_scoreboard_get_tick();

sapi/fpm/fpm/fpm_status.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,10 +472,10 @@ int fpm_status_handle_request(void) /* {{{ */
472472

473473
first = 1;
474474
for (i=0; i<scoreboard_p->nprocs; i++) {
475-
if (!scoreboard_p->procs[i] || !scoreboard_p->procs[i]->used) {
475+
if (!scoreboard_p->procs[i].used) {
476476
continue;
477477
}
478-
proc = *scoreboard_p->procs[i];
478+
proc = scoreboard_p->procs[i];
479479

480480
if (first) {
481481
first = 0;

sapi/fpm/fpm/fpm_worker_pool.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ static void fpm_worker_pool_cleanup(int which, void *arg) /* {{{ */
5454
fpm_worker_pool_config_free(wp->config);
5555
fpm_children_free(wp->children);
5656
if ((which & FPM_CLEANUP_CHILD) == 0 && fpm_globals.parent_pid == getpid()) {
57-
fpm_scoreboard_free(wp->scoreboard);
57+
fpm_scoreboard_free(wp);
5858
}
5959
fpm_worker_pool_free(wp);
6060
}

0 commit comments

Comments
 (0)