-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix bug #71038 - session_start() returns true even when it failed #2167
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
Conversation
…s can be fixed also. - Disallow nonsense function usage that has no effect - Return proper value on failure - Show proper errors on failure Additionally - Made session_flash()/session_commit()/session_write_close() report errors and return bool, parameter is checked as other functions.
Maybe a side thing, but Yasuo, from how I read php_session_start(), then it is possible for it to return SUCCESS and have PS(id) be NULL, is this intended, or? |
@KalleZ Do you mean php_session_start()? Then yes, it is intended. If you read code closely, PS(id) is set eventually for successful call. Otherwise, many PHP sites will have severe problems. When session cannot be started, it results in empty session ID because it does not make sense to have unusable session ID. Is there any valid use case for this? |
@@ -1531,36 +1542,50 @@ PHPAPI void php_session_start(void) /* {{{ */ | |||
PS(id) = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yohgaki I mean here for example, don't we miss a return as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@KalleZ Thank you for checking!
php_session_start() only initializes session ID from client. Invalid session ID or initial access can result in PS(id) == NULL. Therefore there should not be "return" here.
There is php_session_initialize() bellow. It creates new session ID when PS(id) == NULL. If it failed for reasons, it should return FAILURE with PS(id) == NULL or PS(id) != NULL.
As you can see from this PR. Session module has a lot of problems and legacy codes. There may be redundant/strange/obsolete codes. Some are intentional, but please let me know if you find anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yohgaki Thank you for clarifying, the session module does seem a mess at some parts and I was just trying to understand it a bit better. I will let you know if I spot anything obvious.
@yohgaki hmm it may be me then but as I read the code then there is at least one such case where PS(id) can be NULL and php_session_start() be a SUCCESS. I added an inline comment |
STD_PHP_INI_ENTRY("session.referer_check", "", PHP_INI_ALL, OnUpdateString, extern_referer_chk, php_ps_globals, ps_globals) | ||
STD_PHP_INI_ENTRY("session.cache_limiter", "nocache", PHP_INI_ALL, OnUpdateString, cache_limiter, php_ps_globals, ps_globals) | ||
STD_PHP_INI_ENTRY("session.cache_expire", "180", PHP_INI_ALL, OnUpdateLong, cache_expire, php_ps_globals, ps_globals) | ||
STD_PHP_INI_ENTRY("session.cookie_lifetime", "0", PHP_INI_ALL, OnUpdateSessionLongGEZero, cookie_lifetime, php_ps_globals, ps_globals) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OnUpdateSessionLongGEZero makes little sense, OnUpdateSessionLifetime sounds more obvious as name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or even better OnUpdateSessionTTL (TTL is only > 0)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not reused, so it would be better to use dedicated name. I'll
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It kept TTL for other usage and used Lifetime for this.
i.e. Time stamped session management.
Why does travis not fail to build always, but on occasion when base branch is wrong?? It seems multiple tests are running on build env... |
@@ -1353,15 +1402,17 @@ static void ppid2sid(zval *ppid) { | |||
} | |||
} | |||
|
|||
PHPAPI void php_session_reset_id(void) /* {{{ */ | |||
|
|||
/* Made to return int from 7.1, previously void */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to comment on this in the code, this should be in the manual and UPGRADING.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I'll.
static PHP_INI_MH(OnUpdateCookieLifetime) /* {{{ */ | ||
{ | ||
SESSION_CHECK_OUTPUT_STATE; | ||
return OnUpdateLongGEZero(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was OnUpdateLong before, why it changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Negative lifetime is treated the same as 0. It is mitigation for integer overflow that user would like to set very long lifetime, but it overflowed by computation. It's unlikely, so it could be OnUpdateLong. Should I?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PHP numbers don't work this way - if you add two large integers, it converts to float, not negative number.
PHP_INI_ENTRY("session.use_trans_sid", "0", PHP_INI_ALL, OnUpdateTransSid) | ||
PHP_INI_ENTRY("session.sid_length", "32", PHP_INI_ALL, OnUpdateSidLength) | ||
PHP_INI_ENTRY("session.sid_bits_per_character", "4", PHP_INI_ALL, OnUpdateSidBits) | ||
STD_PHP_INI_BOOLEAN("session.lazy_write", "1", PHP_INI_ALL, OnUpdateBool, lazy_write, php_ps_globals, ps_globals) | ||
STD_PHP_INI_BOOLEAN("session.lazy_write", "1", PHP_INI_ALL, OnUpdateSessionBool, lazy_write, php_ps_globals, ps_globals) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why I can't change lazy write after session start? Write happens at the end, not at the start.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lazy_write stores read session data, so this setting should be enabled before session_start(), otherwise it does not take the effect. I should fix this! Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I think still not much of a problem enabling lazy writes - worst thing, it would not be lazy if the data to do lazy part is not there.
php_error_docref(NULL, E_WARNING, "Cannot set cookie parameters - headers already sent"); | ||
RETURN_FALSE; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we also check session state? If session state is wrong, INI functions will fail anyway, so why not check it if we're already checking this?
@@ -1675,6 +1770,11 @@ static PHP_FUNCTION(session_module_name) | |||
return; | |||
} | |||
|
|||
if (name && PS(session_status) == php_session_active) { | |||
php_error_docref(NULL, E_WARNING, "Cannot change save handler module when session is active"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we need this check? According to test bug73100.phpt session_module_name already fails when session is active.
@@ -1867,6 +1968,11 @@ static PHP_FUNCTION(session_save_path) | |||
return; | |||
} | |||
|
|||
if (PS(session_status) == php_session_active) { | |||
php_error_docref(NULL, E_WARNING, "Cannot change save path when session is active"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here - why not?
@@ -1710,7 +1810,8 @@ static PHP_FUNCTION(session_set_save_handler) | |||
zend_string *name; | |||
zend_string *ini_name, *ini_val; | |||
|
|||
if (PS(session_status) != php_session_none) { | |||
if (PS(session_status) == php_session_active) { | |||
php_error_docref(NULL, E_WARNING, "Cannot change save handler when session is active"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we can't change it here, actually? Would something work wrong? Yes, we may use different handlers - so what? Changing write handler before write happens should not be a problem, should it?
@@ -2074,6 +2182,11 @@ static PHP_FUNCTION(session_cache_limiter) | |||
zend_string *limiter = NULL; | |||
zend_string *ini_name; | |||
|
|||
if (PS(session_status) == php_session_active) { | |||
php_error_docref(NULL, E_WARNING, "Cannot change cache limiter when session is active"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I would rather tell about headers since session being active is not the problem here - the problem here would be we already sent the headers, so we can't take them back.
@@ -2177,6 +2295,11 @@ static PHP_FUNCTION(session_start) | |||
RETURN_FALSE; | |||
} | |||
|
|||
if (PS(session_status) == php_session_active) { | |||
php_error_docref(NULL, E_NOTICE, "A session had already been started - ignoring"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure this is right. Certainly a BC break. You may have legitimate case for replacing session data from third party source. Weird, but people do weird things. Is there a reason to prohibit it?
@@ -2227,14 +2356,23 @@ static PHP_FUNCTION(session_destroy) | |||
return; | |||
} | |||
|
|||
if (PS(session_status) != php_session_active) { | |||
php_error_docref(NULL, E_WARNING, "Trying to destroy uninitialized session"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about this one... There might be code that calls destroy repeatedly, just in case - e.g. error handlers, etc. - and putting warning there does not help much. Since the session stays destroyed, there's not real reason to react with a failure error. I'd just silently return, maybe return false at worst. Warning should be when whatever you tried to do failed, but here it didn't actually fail - it was just unnecessary.
…etime. Check active state for lazy_write
… into PHP-7.1-session-bug71038
…nt will never work. Should not change setting while session is active because it does not work.
All issue should be resolved. Please let me know if you find any. All INI changes should not be done while active. Once output is started, both cookie and trans sid session will not work correctly. |
* master: Fix bug #71038 - session_start() returns true even when it failed PR php#2167 Remove PDOStatement::activeQueryString()
@yohgaki Can you add check on no arguments before output warning? thank you. |
@SailorMax With current master
2nd session_name() should not try to send HTTP Cookie header. The referred bug report problem seems this is the cause. Following what it should do. (tested with 7.0)
Let's continue discussion on the |
@yohgaki Same problem with |
@SailorMax |
@SailorMax
SG(headers_sent) should return false always with CLI. However, many test scripts may rely on SG(headers_sent) being true. I thought about disabling the checks for CLI build, but we have CLI server... "if (name && SG(headers_sent)) {", this change is good, but it fixes issue partially for CLI. It may be better leave this as documentation issue for 7.2 because it does not make much sense to optimize behavior for non output buffered CLI, i.e. Web SAPI should enable output buffer to work. Anyway, the "name" check should be added. I'll commit the changes for 5 session functions. |
@SailorMax |
Sorry for late bug fix for important feature.
I felt complete fix requires RFC and I forgot to create RFC for this.
https://bugs.php.net/bug.php?id=71038
In short, this patch fixes session_start()'s insane behaviors.
NOTE: There is no functional changes at all, but only error handling changes. Therefore, proper codes will not be affected by this change. Only bad codes are detected.
Original session_start() is designed to continue execution as much as it can. This design caused a lot of issues including number of crash bugs. We've removed most issues caused by this design, but session_start() behaves insane way. e.g. Return TRUE and initializes $_SESSION array for useless session, improper error messages, memory leak, etc. (Please verify phpt changes how this patch makes session_start() behave sane way)
This fix may change app behavior. However, it changes behavior only when there is useless session which is fatal anyway. Therefore, it could be applied to PHP 7.1.
I pushed patch ( 637f72c and later commits) fixes number of nonsense/inconsistent session function behaviors. Many of them are impossible to fix due to improper session status management. This bug fix allows us to fix many error handling issues in session module. The additional patch is pushed so that it's easy to cherry pick minimum fixes. 637f72c and later commits are additional fixes other than session_start() return value issue.
Change Summary: