New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Corrupted session written when there's a fatal error in autoloader #12504
Comments
Active support for php8.0.x has ended. Regards. |
I confirmed that this is also an issue with PHP 8.1.24 and 8.2.11 too. In the test script above, I updated the type hints on the session handler to get rid of the associated warnings for 8.1 and 8.2. |
Thank you for confirmation. Can you provide a new repro code? |
Oops, it was edited. |
I understand that if there is an output after the error, this php-src/Zend/zend_execute_API.c Line 782 in 2fffb83
I don't know much about this, so maybe someone else can give you a better answer. |
Thanks for looking, @SakiTakamachi. |
I took a look through the code and here's what I found. For existing contributors to the project, they probably already know how the code works, but maybe I'll try to figure out how to debug this myself some day.
Lines 984 to 1030 in 4ae483a
I think this is were the issue is. I notice that it loops through the serialized string and calls
I also see the following code, which I think explains why the variables that are set are zeroed out. php-src/ext/standard/var_unserializer.re Lines 859 to 875 in 4ae483a
The one final thing I notices is that if I add the following to the test code above, it shows that the session is active when the fatal error happens. register_shutdown_function(function() {
echo 'Exiting. Session is ' . (session_status() === PHP_SESSION_ACTIVE ? 'active' : 'not active') . '.' . PHP_EOL;
}); My guess is that Line 400 in 4ae483a
Perhaps that needs to be set back to disabled if the following line fails: Line 477 in 4ae483a
|
Normally, if the serializer function returns FAILURE, then the session will be destroyed. diff --git a/ext/session/session.c b/ext/session/session.c
index 114df24b46..311abe84e6 100644
--- a/ext/session/session.c
+++ b/ext/session/session.c
@@ -242,18 +242,29 @@ static zend_string *php_session_encode(void) /* {{{ */
}
/* }}} */
+static void php_session_cancel_decode(void)
+{
+ php_session_destroy();
+ php_session_track_init();
+ // XXX: debatable if the warning should be here...
+ php_error_docref(NULL, E_WARNING, "Failed to decode session object. Session has been destroyed");
+}
+
static int php_session_decode(zend_string *data) /* {{{ */
{
if (!PS(serializer)) {
php_error_docref(NULL, E_WARNING, "Unknown session.serialize_handler. Failed to decode session object");
return FAILURE;
}
- if (PS(serializer)->decode(ZSTR_VAL(data), ZSTR_LEN(data)) == FAILURE) {
- php_session_destroy();
- php_session_track_init();
- php_error_docref(NULL, E_WARNING, "Failed to decode session object. Session has been destroyed");
- return FAILURE;
- }
+ zend_try {
+ if (PS(serializer)->decode(ZSTR_VAL(data), ZSTR_LEN(data)) == FAILURE) {
+ php_session_cancel_decode();
+ return FAILURE;
+ }
+ } zend_catch {
+ php_session_cancel_decode();
+ zend_bailout();
+ } zend_end_try();
return SUCCESS;
}
/* }}} */
Results in:
Which is more desirable. If you don't want the warning, you move the php_err_docref call. The sad part is that this isn't exactly a light-weight solution, the jump buffers used in zend_try are heavyweight objects and this will thus incur a cost on every session decode. |
Thanks, @nielsdos. Is that something you want to submit a PR for or should I add that try/catch? |
I'm looking for some feedback about the general idea from other contributors before PR'ing that, because it's a bit ad hoc and may have a performance penalty. So I'm holding off for submitting a PR for the moment. |
Sounds good. Thanks! |
I've experimented with different kinds of fixes. I also found that since the session is partially decoded, register_shutdown_function(function() {
var_dump($_SESSION["before"] . 'x');
});
So it seems like the zend_try solution might be the most logical one. |
Using |
Although I might be making a fuzz for nothing, seems like it takes about 7 ns to do the setjump on my system... |
… in autoloader For details and reasoning, see [1] and following. [1] php#12504 (comment)
* PHP-8.2: Fix GH-12504: Corrupted session written when there's a fatal error in autoloader
* PHP-8.3: Fix GH-12504: Corrupted session written when there's a fatal error in autoloader
Description
Create two files in the same directory:
index.php
Test.class.php
When running
index.php
, it will result in a fatal error. However, it will also write a session with all variables before the invalidTest
class set to zero and no variables after the$_SESSION['test']
. You can see that in the output:If you uncomment the second
require_once
so that the autoloader doesn't need to run, it still results in the fatal error as expected, but it does not write back to the session.I know that avoiding the fatal error is a workaround, but if this can be fixed, it could prevent corrupting user sessions if bad code gets deployed to production.
PHP Version
PHP 8.0.30
Operating System
No response
The text was updated successfully, but these errors were encountered: