Skip to content

Commit 7f7031e

Browse files
committed
Fix phpGH-12504: Corrupted session written when there's a fatal error in autoloader
For details and reasoning, see [1] and following. [1] php#12504 (comment) Closes phpGH-13207.
1 parent f120ac9 commit 7f7031e

File tree

3 files changed

+82
-6
lines changed

3 files changed

+82
-6
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ PHP NEWS
3535
. Fixed bug GH-13138 (Randomizer::pickArrayKeys() does not detect broken
3636
engines). (timwolla)
3737

38+
- Session:
39+
. Fixed bug GH-12504 (Corrupted session written when there's a fatal error
40+
in autoloader). (nielsdos)
41+
3842
- Streams:
3943
. Fixed bug GH-13071 (Copying large files using mmap-able source streams may
4044
exhaust available memory and fail). (nielsdos)

ext/session/session.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,18 +246,28 @@ static zend_string *php_session_encode(void) /* {{{ */
246246
}
247247
/* }}} */
248248

249+
static ZEND_COLD void php_session_cancel_decode(void)
250+
{
251+
php_session_destroy();
252+
php_session_track_init();
253+
php_error_docref(NULL, E_WARNING, "Failed to decode session object. Session has been destroyed");
254+
}
255+
249256
static zend_result php_session_decode(zend_string *data) /* {{{ */
250257
{
251258
if (!PS(serializer)) {
252259
php_error_docref(NULL, E_WARNING, "Unknown session.serialize_handler. Failed to decode session object");
253260
return FAILURE;
254261
}
255-
if (PS(serializer)->decode(ZSTR_VAL(data), ZSTR_LEN(data)) == FAILURE) {
256-
php_session_destroy();
257-
php_session_track_init();
258-
php_error_docref(NULL, E_WARNING, "Failed to decode session object. Session has been destroyed");
259-
return FAILURE;
260-
}
262+
zend_try {
263+
if (PS(serializer)->decode(ZSTR_VAL(data), ZSTR_LEN(data)) == FAILURE) {
264+
php_session_cancel_decode();
265+
return FAILURE;
266+
}
267+
} zend_catch {
268+
php_session_cancel_decode();
269+
zend_bailout();
270+
} zend_end_try();
261271
return SUCCESS;
262272
}
263273
/* }}} */

ext/session/tests/gh12504.phpt

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
--TEST--
2+
GH-12504 (Corrupted session written when there's a fatal error in autoloader)
3+
--EXTENSIONS--
4+
session
5+
--FILE--
6+
<?php
7+
8+
class TestSessionHandler implements SessionHandlerInterface
9+
{
10+
public function close(): bool
11+
{
12+
return true;
13+
}
14+
public function destroy(string $id): bool
15+
{
16+
return true;
17+
}
18+
public function gc(int $max_lifetime): int|false
19+
{
20+
return 0;
21+
}
22+
public function open(string $path, string $name): bool
23+
{
24+
return true;
25+
}
26+
public function read(string $id): string|false
27+
{
28+
// Return a session object that has 3 variables
29+
return 'before|i:1234;test|O:4:"Test":0:{}after|i:5678;';
30+
}
31+
public function write($id, $data): bool
32+
{
33+
echo 'Write session:' . PHP_EOL;
34+
echo $data . PHP_EOL;
35+
return true;
36+
}
37+
}
38+
39+
register_shutdown_function(function() {
40+
echo "In shutdown function\n";
41+
var_dump($_SESSION);
42+
});
43+
44+
session_set_save_handler(new TestSessionHandler());
45+
46+
// Autoload class that's in session
47+
spl_autoload_register(function() {
48+
// Easiest way to reproduce the issue is to dynamically define a class with a bogus definition
49+
eval('class Test {public int $var = null;}');
50+
return true;
51+
});
52+
53+
session_start();
54+
55+
?>
56+
--EXPECTF--
57+
Fatal error: Default value for property of type int may not be null. Use the nullable type ?int to allow null default value in %s on line %d
58+
59+
Warning: Unknown: Failed to decode session object. Session has been destroyed in Unknown on line 0
60+
In shutdown function
61+
array(0) {
62+
}

0 commit comments

Comments
 (0)