Skip to content

Commit c561f7d

Browse files
committed
Refresh zend_mm shadow key on fork
The shadow key is refreshed when resetting the memory manager between two requests. But in forking SAPIs the first request of a child process inherits the shadow key of the parent. As a result, a leak of the shadow key during the first request of one process gives away the shadow key used during the first request of other processes. This makes the key refresh mechanism less useful. Here I ensure that we refresh the shadow key after a fork. We can not reset the manager as there may be active allocations. Instead, we have to recompute shadow pointers with the new key. Closes GH-16765
1 parent 8e38f6d commit c561f7d

File tree

12 files changed

+85
-13
lines changed

12 files changed

+85
-13
lines changed

UPGRADING.INTERNALS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ PHP 8.5 INTERNALS UPGRADE NOTES
7373
. EG(fake_scope) now is a _const_ zend_class_entry*.
7474
. zend_begin_record_errors() or EG(record_errors)=true cause errors to be
7575
delayed. Before, errors would be recorded but not delayed.
76+
. zend_mm_refresh_key_child() must be called on any zend_mm_heap inherited
77+
from the parent process after a fork().
7678

7779
========================
7880
2. Build system changes
@@ -148,3 +150,7 @@ PHP 8.5 INTERNALS UPGRADE NOTES
148150
========================
149151
5. SAPI changes
150152
========================
153+
154+
- SAPIs must now call php_child_init() after a fork. If php-src code was
155+
executed in other threads than the one initiating the fork,
156+
refresh_memory_manager() must be called in every such thread.

Zend/zend_alloc.c

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,9 @@ struct _zend_mm_heap {
317317
} debug;
318318
};
319319
#endif
320+
#if ZEND_DEBUG
320321
pid_t pid;
322+
#endif
321323
zend_random_bytes_insecure_state rand_state;
322324
};
323325

@@ -1310,15 +1312,20 @@ static zend_always_inline zend_mm_free_slot* zend_mm_encode_free_slot(const zend
13101312
#endif
13111313
}
13121314

1313-
static zend_always_inline zend_mm_free_slot* zend_mm_decode_free_slot(zend_mm_heap *heap, zend_mm_free_slot *slot)
1315+
static zend_always_inline zend_mm_free_slot* zend_mm_decode_free_slot_key(uintptr_t shadow_key, zend_mm_free_slot *slot)
13141316
{
13151317
#ifdef WORDS_BIGENDIAN
1316-
return (zend_mm_free_slot*)((uintptr_t)slot ^ heap->shadow_key);
1318+
return (zend_mm_free_slot*)((uintptr_t)slot ^ shadow_key);
13171319
#else
1318-
return (zend_mm_free_slot*)(BSWAPPTR((uintptr_t)slot ^ heap->shadow_key));
1320+
return (zend_mm_free_slot*)(BSWAPPTR((uintptr_t)slot ^ shadow_key));
13191321
#endif
13201322
}
13211323

1324+
static zend_always_inline zend_mm_free_slot* zend_mm_decode_free_slot(zend_mm_heap *heap, zend_mm_free_slot *slot)
1325+
{
1326+
return zend_mm_decode_free_slot_key(heap->shadow_key, slot);
1327+
}
1328+
13221329
static zend_always_inline void zend_mm_set_next_free_slot(zend_mm_heap *heap, uint32_t bin_num, zend_mm_free_slot *slot, zend_mm_free_slot *next)
13231330
{
13241331
ZEND_ASSERT(bin_data_size[bin_num] >= ZEND_MM_MIN_USEABLE_BIN_SIZE);
@@ -2027,6 +2034,34 @@ static void zend_mm_init_key(zend_mm_heap *heap)
20272034
zend_mm_refresh_key(heap);
20282035
}
20292036

2037+
ZEND_API void zend_mm_refresh_key_child(zend_mm_heap *heap)
2038+
{
2039+
uintptr_t old_key = heap->shadow_key;
2040+
2041+
zend_mm_init_key(heap);
2042+
2043+
/* Update shadow pointers with new key */
2044+
for (int i = 0; i < ZEND_MM_BINS; i++) {
2045+
zend_mm_free_slot *slot = heap->free_slot[i];
2046+
if (!slot) {
2047+
continue;
2048+
}
2049+
zend_mm_free_slot *next;
2050+
while ((next = slot->next_free_slot)) {
2051+
zend_mm_free_slot *shadow = ZEND_MM_FREE_SLOT_PTR_SHADOW(slot, i);
2052+
if (UNEXPECTED(next != zend_mm_decode_free_slot_key(old_key, shadow))) {
2053+
zend_mm_panic("zend_mm_heap corrupted");
2054+
}
2055+
zend_mm_set_next_free_slot(heap, i, slot, next);
2056+
slot = next;
2057+
}
2058+
}
2059+
2060+
#if ZEND_DEBUG
2061+
heap->pid = getpid();
2062+
#endif
2063+
}
2064+
20302065
static zend_mm_heap *zend_mm_init(void)
20312066
{
20322067
zend_mm_chunk *chunk = (zend_mm_chunk*)zend_mm_chunk_alloc_int(ZEND_MM_CHUNK_SIZE, ZEND_MM_CHUNK_SIZE);
@@ -2075,7 +2110,9 @@ static zend_mm_heap *zend_mm_init(void)
20752110
heap->storage = NULL;
20762111
#endif
20772112
heap->huge_list = NULL;
2113+
#if ZEND_DEBUG
20782114
heap->pid = getpid();
2115+
#endif
20792116
return heap;
20802117
}
20812118

@@ -2535,13 +2572,12 @@ ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
25352572
p->free_map[0] = (1L << ZEND_MM_FIRST_PAGE) - 1;
25362573
p->map[0] = ZEND_MM_LRUN(ZEND_MM_FIRST_PAGE);
25372574

2538-
pid_t pid = getpid();
2539-
if (heap->pid != pid) {
2540-
zend_mm_init_key(heap);
2541-
heap->pid = pid;
2542-
} else {
2543-
zend_mm_refresh_key(heap);
2544-
}
2575+
#if ZEND_DEBUG
2576+
ZEND_ASSERT(getpid() == heap->pid
2577+
&& "heap was re-used without calling zend_mm_refresh_key_child() after a fork");
2578+
#endif
2579+
2580+
zend_mm_refresh_key(heap);
25452581
}
25462582
}
25472583

@@ -2949,6 +2985,11 @@ ZEND_API void shutdown_memory_manager(bool silent, bool full_shutdown)
29492985
zend_mm_shutdown(AG(mm_heap), full_shutdown, silent);
29502986
}
29512987

2988+
ZEND_API void refresh_memory_manager(void)
2989+
{
2990+
zend_mm_refresh_key_child(AG(mm_heap));
2991+
}
2992+
29522993
static ZEND_COLD ZEND_NORETURN void zend_out_of_memory(void)
29532994
{
29542995
fprintf(stderr, "Out of memory\n");
@@ -3506,7 +3547,9 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void
35063547
memcpy(storage->data, data, data_size);
35073548
}
35083549
heap->storage = storage;
3550+
#if ZEND_DEBUG
35093551
heap->pid = getpid();
3552+
#endif
35103553
return heap;
35113554
#else
35123555
return NULL;

Zend/zend_alloc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ ZEND_API bool zend_alloc_in_memory_limit_error_reporting(void);
220220

221221
ZEND_API void start_memory_manager(void);
222222
ZEND_API void shutdown_memory_manager(bool silent, bool full_shutdown);
223+
ZEND_API void refresh_memory_manager(void);
223224
ZEND_API bool is_zend_mm(void);
224225
ZEND_API bool is_zend_ptr(const void *ptr);
225226

@@ -316,6 +317,8 @@ struct _zend_mm_storage {
316317
ZEND_API zend_mm_storage *zend_mm_get_storage(zend_mm_heap *heap);
317318
ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void *data, size_t data_size);
318319

320+
ZEND_API void zend_mm_refresh_key_child(zend_mm_heap *heap);
321+
319322
/*
320323
321324
// The following example shows how to use zend_mm_heap API with custom storage

ext/pcntl/pcntl.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "php_signal.h"
3333
#include "php_ticks.h"
3434
#include "zend_fibers.h"
35+
#include "main/php_main.h"
3536

3637
#if defined(HAVE_GETPRIORITY) || defined(HAVE_SETPRIORITY) || defined(HAVE_WAIT3)
3738
#include <sys/wait.h>
@@ -297,7 +298,7 @@ PHP_FUNCTION(pcntl_fork)
297298

298299
}
299300
} else if (id == 0) {
300-
zend_max_execution_timer_init();
301+
php_child_init();
301302
}
302303

303304
RETURN_LONG((zend_long) id);
@@ -1560,7 +1561,7 @@ PHP_FUNCTION(pcntl_rfork)
15601561
php_error_docref(NULL, E_WARNING, "Error %d", errno);
15611562
}
15621563
} else if (pid == 0) {
1563-
zend_max_execution_timer_init();
1564+
php_child_init();
15641565
}
15651566

15661567
RETURN_LONG((zend_long) pid);
@@ -1605,7 +1606,7 @@ PHP_FUNCTION(pcntl_forkx)
16051606
php_error_docref(NULL, E_WARNING, "Error %d", errno);
16061607
}
16071608
} else if (pid == 0) {
1608-
zend_max_execution_timer_init();
1609+
php_child_init();
16091610
}
16101611

16111612
RETURN_LONG((zend_long) pid);

main/main.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,12 @@ static void sigchld_handler(int apar)
18161816
/* }}} */
18171817
#endif
18181818

1819+
PHPAPI void php_child_init(void)
1820+
{
1821+
refresh_memory_manager();
1822+
zend_max_execution_timer_init();
1823+
}
1824+
18191825
/* {{{ php_request_startup */
18201826
zend_result php_request_startup(void)
18211827
{

main/php_main.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ ZEND_ATTRIBUTE_CONST PHPAPI const char *php_build_provider(void);
4949
PHPAPI char *php_get_version(sapi_module_struct *sapi_module);
5050
PHPAPI void php_print_version(sapi_module_struct *sapi_module);
5151

52+
PHPAPI void php_child_init(void);
5253
PHPAPI zend_result php_request_startup(void);
5354
PHPAPI void php_request_shutdown(void *dummy);
5455
PHPAPI zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additional_module);

sapi/apache2handler/sapi_apache2.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,7 @@ zend_first_try {
752752
static void php_apache_child_init(apr_pool_t *pchild, server_rec *s)
753753
{
754754
apr_pool_cleanup_register(pchild, NULL, php_apache_child_shutdown, apr_pool_cleanup_null);
755+
php_child_init();
755756
}
756757

757758
#ifdef ZEND_SIGNALS

sapi/cgi/cgi_main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,8 @@ consult the installation file that came with this distribution, or visit \n\
20412041
*/
20422042
parent = 0;
20432043

2044+
php_child_init();
2045+
20442046
/* don't catch our signals */
20452047
sigaction(SIGTERM, &old_term, 0);
20462048
sigaction(SIGQUIT, &old_quit, 0);

sapi/cli/php_cli_server.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2530,6 +2530,7 @@ static void php_cli_server_startup_workers(void) {
25302530
#if defined(HAVE_PRCTL) || defined(HAVE_PROCCTL)
25312531
php_cli_server_worker_install_pdeathsig();
25322532
#endif
2533+
php_child_init();
25332534
return;
25342535
} else {
25352536
php_cli_server_workers[php_cli_server_worker] = pid;

sapi/fpm/fpm/fpm_php.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,9 @@ int fpm_php_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
253253
limit_extensions = wp->limit_extensions;
254254
wp->limit_extensions = NULL;
255255
}
256+
257+
php_child_init();
258+
256259
return 0;
257260
}
258261
/* }}} */

0 commit comments

Comments
 (0)