Skip to content

Commit 304c371

Browse files
committed
Refresh zend_mm shadow key on fork
The memory manager is cleaned up after each request by calling shutdown_memory_manager(). At the same time, this prepares the manager for the next request, and the shadow key is refreshed. In forking SAPIs the first request of every child process inherits the memory manager of the parent process, including the shadow key. 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. The memory manager is not empty at this point (we perform allocations after shutdown_memory_manager()), so we have to recompute any shadow pointers with the new key. We assume that the parent process had only one PHP thread running, as anything else would be unsafe. If a SAPI forks a process running more than one PHP thread, it is its responsibility to call refresh_memory_manager() in each PHP thread of the child process. Closes GH-16765
1 parent 7b3e68f commit 304c371

File tree

11 files changed

+62
-18
lines changed

11 files changed

+62
-18
lines changed

Zend/zend_alloc.c

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@
7070
# include <wincrypt.h>
7171
# include <process.h>
7272
# include "win32/winutil.h"
73-
# define getpid _getpid
74-
typedef int pid_t;
7573
#endif
7674

7775
#include <stdio.h>
@@ -317,7 +315,6 @@ struct _zend_mm_heap {
317315
} debug;
318316
};
319317
#endif
320-
pid_t pid;
321318
zend_random_bytes_insecure_state rand_state;
322319
};
323320

@@ -1310,15 +1307,20 @@ static zend_always_inline zend_mm_free_slot* zend_mm_encode_free_slot(const zend
13101307
#endif
13111308
}
13121309

1313-
static zend_always_inline zend_mm_free_slot* zend_mm_decode_free_slot(zend_mm_heap *heap, zend_mm_free_slot *slot)
1310+
static zend_always_inline zend_mm_free_slot* zend_mm_decode_free_slot_key(uintptr_t shadow_key, zend_mm_free_slot *slot)
13141311
{
13151312
#ifdef WORDS_BIGENDIAN
1316-
return (zend_mm_free_slot*)((uintptr_t)slot ^ heap->shadow_key);
1313+
return (zend_mm_free_slot*)((uintptr_t)slot ^ shadow_key);
13171314
#else
1318-
return (zend_mm_free_slot*)(BSWAPPTR((uintptr_t)slot ^ heap->shadow_key));
1315+
return (zend_mm_free_slot*)(BSWAPPTR((uintptr_t)slot ^ shadow_key));
13191316
#endif
13201317
}
13211318

1319+
static zend_always_inline zend_mm_free_slot* zend_mm_decode_free_slot(zend_mm_heap *heap, zend_mm_free_slot *slot)
1320+
{
1321+
return zend_mm_decode_free_slot_key(heap->shadow_key, slot);
1322+
}
1323+
13221324
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)
13231325
{
13241326
ZEND_ASSERT(bin_data_size[bin_num] >= ZEND_MM_MIN_USEABLE_BIN_SIZE);
@@ -2027,6 +2029,30 @@ static void zend_mm_init_key(zend_mm_heap *heap)
20272029
zend_mm_refresh_key(heap);
20282030
}
20292031

2032+
static void zend_mm_refresh_key_child(zend_mm_heap *heap)
2033+
{
2034+
uintptr_t old_key = heap->shadow_key;
2035+
2036+
zend_mm_init_key(heap);
2037+
2038+
/* Update shadow pointers with new key */
2039+
for (int i = 0; i < ZEND_MM_BINS; i++) {
2040+
zend_mm_free_slot *slot = heap->free_slot[i];
2041+
if (!slot) {
2042+
continue;
2043+
}
2044+
zend_mm_free_slot *next;
2045+
while ((next = slot->next_free_slot)) {
2046+
zend_mm_free_slot *shadow = ZEND_MM_FREE_SLOT_PTR_SHADOW(slot, i);
2047+
if (UNEXPECTED(next != zend_mm_decode_free_slot_key(old_key, shadow))) {
2048+
zend_mm_panic("zend_mm_heap corrupted");
2049+
}
2050+
zend_mm_set_next_free_slot(heap, i, slot, next);
2051+
slot = next;
2052+
}
2053+
}
2054+
}
2055+
20302056
static zend_mm_heap *zend_mm_init(void)
20312057
{
20322058
zend_mm_chunk *chunk = (zend_mm_chunk*)zend_mm_chunk_alloc_int(ZEND_MM_CHUNK_SIZE, ZEND_MM_CHUNK_SIZE);
@@ -2075,7 +2101,6 @@ static zend_mm_heap *zend_mm_init(void)
20752101
heap->storage = NULL;
20762102
#endif
20772103
heap->huge_list = NULL;
2078-
heap->pid = getpid();
20792104
return heap;
20802105
}
20812106

@@ -2535,13 +2560,7 @@ ZEND_API void zend_mm_shutdown(zend_mm_heap *heap, bool full, bool silent)
25352560
p->free_map[0] = (1L << ZEND_MM_FIRST_PAGE) - 1;
25362561
p->map[0] = ZEND_MM_LRUN(ZEND_MM_FIRST_PAGE);
25372562

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-
}
2563+
zend_mm_refresh_key(heap);
25452564
}
25462565
}
25472566

@@ -2949,6 +2968,11 @@ ZEND_API void shutdown_memory_manager(bool silent, bool full_shutdown)
29492968
zend_mm_shutdown(AG(mm_heap), full_shutdown, silent);
29502969
}
29512970

2971+
ZEND_API void refresh_memory_manager(void)
2972+
{
2973+
zend_mm_refresh_key_child(AG(mm_heap));
2974+
}
2975+
29522976
static ZEND_COLD ZEND_NORETURN void zend_out_of_memory(void)
29532977
{
29542978
fprintf(stderr, "Out of memory\n");
@@ -3506,7 +3530,6 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void
35063530
memcpy(storage->data, data, data_size);
35073531
}
35083532
heap->storage = storage;
3509-
heap->pid = getpid();
35103533
return heap;
35113534
#else
35123535
return NULL;

Zend/zend_alloc.h

Lines changed: 1 addition & 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

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
/* }}} */

sapi/litespeed/lsapi_main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,8 @@ void start_children( int children )
13951395
switch( pid ) {
13961396
case 0: /* children process */
13971397

1398+
php_child_init();
1399+
13981400
/* don't catch our signals */
13991401
sigaction( SIGTERM, &old_term, 0 );
14001402
sigaction( SIGQUIT, &old_quit, 0 );

0 commit comments

Comments
 (0)