Skip to content
Open
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
6 changes: 6 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ PHP 8.7 INTERNALS UPGRADE NOTES
1. Internal API changes
========================

- Added zend_module_entry.child_init_func, an optional void callback invoked by
php_child_init() in the child process after core child initialization. It runs
in module dependency order for started modules and may run before a request
starts or during an active request. Use
STANDARD_MODULE_PROPERTIES_EX_WITH_CHILD_INIT(callback) to initialize it.
Modules without a callback continue to use STANDARD_MODULE_PROPERTIES_EX.
- Removed the ZEND_LOG_XOR() macro.
- Removed the ZEND_TRUTH() macro.
- Removed zend_execute_scripts(). Manually call zend_execute_script() in a loop
Expand Down
9 changes: 7 additions & 2 deletions Zend/zend_modules.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
#define ZEND_MODULE_INFO_FUNC_ARGS zend_module_entry *zend_module
#define ZEND_MODULE_INFO_FUNC_ARGS_PASSTHRU zend_module

#define ZEND_MODULE_API_NO 20260925
#define ZEND_MODULE_API_NO 20261024
#ifdef ZTS
#define USING_ZTS 1
#else
Expand All @@ -45,7 +45,11 @@

#define ZEND_MODULE_BUILD_ID "API" ZEND_TOSTR(ZEND_MODULE_API_NO) ZEND_BUILD_TS ZEND_BUILD_DEBUG ZEND_BUILD_SYSTEM ZEND_BUILD_EXTRA

#define STANDARD_MODULE_PROPERTIES_EX 0, 0, NULL, 0, ZEND_MODULE_BUILD_ID
#define STANDARD_MODULE_PROPERTIES_EX_WITH_CHILD_INIT(child_init) \
0, 0, NULL, 0, ZEND_MODULE_BUILD_ID, child_init

#define STANDARD_MODULE_PROPERTIES_EX \
STANDARD_MODULE_PROPERTIES_EX_WITH_CHILD_INIT(NULL)

#define NO_MODULE_GLOBALS 0, NULL, NULL, NULL

Expand Down Expand Up @@ -96,6 +100,7 @@ struct _zend_module_entry {
void *handle;
int module_number;
const char *build_id;
void (*child_init_func)(void);
};

#define MODULE_DEP_REQUIRED 1
Expand Down
2 changes: 0 additions & 2 deletions ext/session/php_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,6 @@ typedef struct _php_ps_globals {
bool mod_user_is_open;
bool mod_user_uses_object_methods_as_handlers;
bool use_trans_sid; /* contains the INI value of whether to use trans-sid */
bool random_seeded;

bool in_rfc1867_update; /* php_session_rfc1867_update() is running */
} php_ps_globals;

Expand Down
31 changes: 17 additions & 14 deletions ext/session/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -400,18 +400,6 @@ static zend_long php_session_gc(bool immediate)
if ((PS(mod_data) || PS(mod_user_implemented))) {
/* Use probability-based GC if not forced and probability is configured */
if (!collect && PS(gc_probability) > 0) {
/* Seed lazily on first GC draw per process. */
if (UNEXPECTED(!PS(random_seeded))) {
php_random_uint128_t seed;
if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
seed = php_random_uint128_constant(
php_random_generate_fallback_seed(),
php_random_generate_fallback_seed()
);
}
php_random_pcgoneseq128xslrr64_seed128(PS(random).state, seed);
PS(random_seeded) = true;
}
collect = php_random_range(PS(random), 0, PS(gc_divisor) - 1) < PS(gc_probability);
}

Expand Down Expand Up @@ -3010,7 +2998,15 @@ static PHP_GINIT_FUNCTION(ps)
.algo = &php_random_algo_pcgoneseq128xslrr64,
.state = &ps_globals->random_state,
};
ps_globals->random_seeded = false;

php_random_uint128_t seed;
if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
seed = php_random_uint128_constant(
php_random_generate_fallback_seed(),
php_random_generate_fallback_seed()
);
}
php_random_pcgoneseq128xslrr64_seed128(ps_globals->random.state, seed);
}

/* Interfaces extending the given one are not flattened into ce->interfaces before they are
Expand Down Expand Up @@ -3445,6 +3441,13 @@ static zend_result php_session_rfc1867_callback(unsigned int event, void *event_
return retval;
}

static void php_session_child_init(void)
{
/* Give each child a distinct, widely separated GC sequence without requesting new entropy. */
uint64_t advance = (uint64_t) getpid() << 32;
php_random_pcgoneseq128xslrr64_advance(PS(random).state, advance);
}

zend_module_entry session_module_entry = {
STANDARD_MODULE_HEADER,
"session",
Expand All @@ -3457,7 +3460,7 @@ zend_module_entry session_module_entry = {
PHP_GINIT(ps),
NULL,
NULL,
STANDARD_MODULE_PROPERTIES_EX
STANDARD_MODULE_PROPERTIES_EX_WITH_CHILD_INIT(php_session_child_init)
};

#ifdef COMPILE_DL_SESSION
Expand Down
99 changes: 99 additions & 0 deletions ext/session/tests/gh22616.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
--TEST--
GH-22616: Session GC randomness is independent after a fork
--EXTENSIONS--
session
pcntl
--INI--
session.gc_probability=1
session.gc_divisor=2
session.use_cookies=0
session.cache_limiter=
--FILE--
<?php
$handler = new class implements SessionHandlerInterface, SessionIdInterface, SessionUpdateTimestampHandlerInterface {
public bool $collected = false;

public function create_sid(): string { return 'gh22616'; }
public function validateId(string $id): bool { return true; }
public function updateTimestamp(string $id, string $data): bool { return true; }
public function open(string $path, string $name): bool { return true; }
public function close(): bool { return true; }
public function read(string $id): string|false { return ''; }
public function write(string $id, string $data): bool { return true; }
public function destroy(string $id): bool { return true; }
public function gc(int $max_lifetime): int|false {
$this->collected = true;
return 0;
}
};
session_set_save_handler($handler);
session_id('gh22616');

function sequence(): string {
global $handler;
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();
}
$result = '';
for ($i = 0; $i < 256; $i++) {
$handler->collected = false;
session_start();
session_write_close();
$result .= (int) $handler->collected;
}
return $result;
}

$results = [];
foreach (['unseeded', 'seeded', 'active'] as $state) {
if ($state === 'active') {
session_start();
}
$file1 = tempnam(sys_get_temp_dir(), 'gh22616');
$pid1 = pcntl_fork();
if ($pid1 === -1) {
throw new Exception('fork failed');
}
if ($pid1 === 0) {
file_put_contents($file1, sequence());
exit;
}

$file2 = tempnam(sys_get_temp_dir(), 'gh22616');
$pid2 = pcntl_fork();
if ($pid2 === -1) {
throw new Exception('fork failed');
}
if ($pid2 === 0) {
file_put_contents($file2, sequence());
exit;
}

$parent = sequence();
pcntl_waitpid($pid1, $status1);
pcntl_waitpid($pid2, $status2);
$child1 = file_get_contents($file1);
$child2 = file_get_contents($file2);
unlink($file1);
unlink($file2);
$results[$state] = pcntl_wifexited($status1)
&& pcntl_wexitstatus($status1) === 0
&& pcntl_wifexited($status2)
&& pcntl_wexitstatus($status2) === 0
&& strlen($child1) === 256
&& strlen($child2) === 256
&& $parent !== $child1
&& $parent !== $child2
&& $child1 !== $child2;
}
var_dump($results);
?>
--EXPECT--
array(3) {
["unseeded"]=>
bool(true)
["seeded"]=>
bool(true)
["active"]=>
bool(true)
}
1 change: 1 addition & 0 deletions ext/zend_test/php_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ ZEND_TSRMLS_CACHE_EXTERN()
#endif

ZEND_BEGIN_MODULE_GLOBALS(zend_test)
zend_long child_init_count;
int observer_enabled;
int observer_show_output;
int observer_observe_all;
Expand Down
14 changes: 13 additions & 1 deletion ext/zend_test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1603,6 +1603,13 @@ static PHP_INI_MH(OnUpdateZendTestObserveOplineInZendMM)
return OnUpdateBool(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage);
}

static ZEND_FUNCTION(zend_test_child_init_count)
{
ZEND_PARSE_PARAMETERS_NONE();

RETURN_LONG(ZT_G(child_init_count));
}

static ZEND_FUNCTION(zend_test_fill_packed_array)
{
HashTable *parameter;
Expand Down Expand Up @@ -2495,6 +2502,11 @@ PHP_MINFO_FUNCTION(zend_test)
DISPLAY_INI_ENTRIES();
}

static void zend_test_child_init(void)
{
ZT_G(child_init_count)++;
}

zend_module_entry zend_test_module_entry = {
STANDARD_MODULE_HEADER,
"zend_test",
Expand All @@ -2509,7 +2521,7 @@ zend_module_entry zend_test_module_entry = {
PHP_GINIT(zend_test),
PHP_GSHUTDOWN(zend_test),
NULL,
STANDARD_MODULE_PROPERTIES_EX
STANDARD_MODULE_PROPERTIES_EX_WITH_CHILD_INIT(zend_test_child_init)
};

#ifdef COMPILE_DL_ZEND_TEST
Expand Down
2 changes: 2 additions & 0 deletions ext/zend_test/test.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ function zend_test_nodiscard(): int {}
#[\NoDiscard(message: "custom message 2")]
function zend_test_deprecated_nodiscard(): int {}

function zend_test_child_init_count(): int {}

/** @alias zend_test_void_return */
function zend_test_aliased(): void {}

Expand Down
6 changes: 5 additions & 1 deletion ext/zend_test/test_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions ext/zend_test/test_decl.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion ext/zend_test/test_legacy_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions ext/zend_test/tests/child_init.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--TEST--
GH-22616: Extension child initialization runs once per fork in the child
--EXTENSIONS--
zend_test
pcntl
--FILE--
<?php
var_dump(zend_test_child_init_count());
$pid = pcntl_fork();
if ($pid === -1) {
throw new Exception('fork failed');
}
if ($pid === 0) {
var_dump(zend_test_child_init_count());
$pid = pcntl_fork();
if ($pid === -1) {
throw new Exception('fork failed');
}
if ($pid === 0) {
var_dump(zend_test_child_init_count());
exit;
}
pcntl_waitpid($pid, $status);
var_dump(pcntl_wifexited($status) && pcntl_wexitstatus($status) === 0);
var_dump(zend_test_child_init_count());
exit;
}
pcntl_waitpid($pid, $status);
var_dump(pcntl_wifexited($status) && pcntl_wexitstatus($status) === 0);
var_dump(zend_test_child_init_count());
?>
--EXPECT--
int(0)
int(1)
int(2)
bool(true)
int(1)
bool(true)
int(0)
Loading
Loading