Skip to content

Commit 180f785

Browse files
authored
Note where a session was already started (#10736)
* Note where a session was already started Duplicated session starts can be annoying to debug. The error that occurs when a session is already active doesn't tell you where it was initialized, so figuring out the callsite involves manual debugging to find it out. This keeps track of the call site of session_start as a request global, and frees at the end of the request. It should make it easier to find these instances for PHP users. The resulting message can look like: Notice: session_start(): Ignoring session_start() because a session is already active (started from /home/calvin/src/php-src/inc.php on line 4) in /home/calvin/src/php-src/index.php on line 9 Fixes GH-10721 * Convert to using zend_string for session start location * Fix leak with session start callsite filename If this was already initialized, we'd forget it. Have shared free between session_start and RSHUTDOWN. * For sessions that are automatically started, note that Easy to forget that you have this set, in which case, session start is done at RINIT outside of user code. Because this config option can't change at runtime, we can check for it and make the error more specific if that's the case.
1 parent 7623bf0 commit 180f785

File tree

4 files changed

+45
-7
lines changed

4 files changed

+45
-7
lines changed

ext/session/php_session.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ typedef struct _php_ps_globals {
154154
const ps_module *default_mod;
155155
void *mod_data;
156156
php_session_status session_status;
157+
zend_string *session_started_filename;
158+
uint32_t session_started_lineno;
157159
zend_long gc_probability;
158160
zend_long gc_divisor;
159161
zend_long gc_maxlifetime;

ext/session/session.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,16 @@ static inline void php_rinit_session_globals(void) /* {{{ */
121121
}
122122
/* }}} */
123123

124+
static inline void php_session_cleanup_filename(void) /* {{{ */
125+
{
126+
if (PS(session_started_filename)) {
127+
zend_string_release(PS(session_started_filename));
128+
PS(session_started_filename) = NULL;
129+
PS(session_started_lineno) = 0;
130+
}
131+
}
132+
/* }}} */
133+
124134
/* Dispatched by RSHUTDOWN and by php_session_destroy */
125135
static inline void php_rshutdown_session_globals(void) /* {{{ */
126136
{
@@ -149,6 +159,8 @@ static inline void php_rshutdown_session_globals(void) /* {{{ */
149159
PS(mod_user_class_name) = NULL;
150160
}
151161

162+
php_session_cleanup_filename();
163+
152164
/* User save handlers may end up directly here by misuse, bugs in user script, etc. */
153165
/* Set session status to prevent error while restoring save handler INI value. */
154166
PS(session_status) = php_session_none;
@@ -465,6 +477,13 @@ static zend_result php_session_initialize(void) /* {{{ */
465477
php_session_decode(val);
466478
zend_string_release_ex(val, 0);
467479
}
480+
481+
php_session_cleanup_filename();
482+
zend_string *session_started_filename = zend_get_executed_filename_ex();
483+
if (session_started_filename != NULL) {
484+
PS(session_started_filename) = zend_string_copy(session_started_filename);
485+
PS(session_started_lineno) = zend_get_executed_lineno();
486+
}
468487
return SUCCESS;
469488
}
470489
/* }}} */
@@ -1490,7 +1509,14 @@ PHPAPI zend_result php_session_start(void) /* {{{ */
14901509

14911510
switch (PS(session_status)) {
14921511
case php_session_active:
1493-
php_error(E_NOTICE, "Ignoring session_start() because a session has already been started");
1512+
if (PS(session_started_filename)) {
1513+
php_error(E_NOTICE, "Ignoring session_start() because a session has already been started (started from %s on line %"PRIu32")", ZSTR_VAL(PS(session_started_filename)), PS(session_started_lineno));
1514+
} else if (PS(auto_start)) {
1515+
/* This option can't be changed at runtime, so we can assume it's because of this */
1516+
php_error(E_NOTICE, "Ignoring session_start() because a session has already been started automatically");
1517+
} else {
1518+
php_error(E_NOTICE, "Ignoring session_start() because a session has already been started");
1519+
}
14941520
return FAILURE;
14951521
break;
14961522

@@ -1600,6 +1626,7 @@ PHPAPI zend_result php_session_start(void) /* {{{ */
16001626
}
16011627
return FAILURE;
16021628
}
1629+
16031630
return SUCCESS;
16041631
}
16051632
/* }}} */
@@ -2513,7 +2540,14 @@ PHP_FUNCTION(session_start)
25132540
}
25142541

25152542
if (PS(session_status) == php_session_active) {
2516-
php_error_docref(NULL, E_NOTICE, "Ignoring session_start() because a session is already active");
2543+
if (PS(session_started_filename)) {
2544+
php_error_docref(NULL, E_NOTICE, "Ignoring session_start() because a session is already active (started from %s on line %"PRIu32")", ZSTR_VAL(PS(session_started_filename)), PS(session_started_lineno));
2545+
} else if (PS(auto_start)) {
2546+
/* This option can't be changed at runtime, so we can assume it's because of this */
2547+
php_error_docref(NULL, E_NOTICE, "Ignoring session_start() because a session is already automatically active");
2548+
} else {
2549+
php_error_docref(NULL, E_NOTICE, "Ignoring session_start() because a session is already active");
2550+
}
25172551
RETURN_TRUE;
25182552
}
25192553

@@ -2846,6 +2880,8 @@ static PHP_MINIT_FUNCTION(session) /* {{{ */
28462880
PS(module_number) = module_number;
28472881

28482882
PS(session_status) = php_session_none;
2883+
PS(session_started_filename) = NULL;
2884+
PS(session_started_lineno) = 0;
28492885
REGISTER_INI_ENTRIES();
28502886

28512887
#ifdef HAVE_LIBMM

ext/session/tests/session_start_variation1.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,15 @@ ob_end_flush();
2525
*** Testing session_start() : variation ***
2626
bool(true)
2727

28-
Notice: session_start(): Ignoring session_start() because a session is already active in %s on line %d
28+
Notice: session_start(): Ignoring session_start() because a session is already active (started from %s on line %d) in %s on line %d
2929
bool(true)
3030

31-
Notice: session_start(): Ignoring session_start() because a session is already active in %s on line %d
31+
Notice: session_start(): Ignoring session_start() because a session is already active (started from %s on line %d) in %s on line %d
3232
bool(true)
3333

34-
Notice: session_start(): Ignoring session_start() because a session is already active in %s on line %d
34+
Notice: session_start(): Ignoring session_start() because a session is already active (started from %s on line %d) in %s on line %d
3535
bool(true)
3636

37-
Notice: session_start(): Ignoring session_start() because a session is already active in %s on line %d
37+
Notice: session_start(): Ignoring session_start() because a session is already active (started from %s on line %d) in %s on line %d
3838
bool(true)
3939
Done

ext/session/tests/session_start_variation9.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ ob_end_flush();
2626
*** Testing session_start() : variation ***
2727
string(%d) "%s"
2828

29-
Notice: session_start(): Ignoring session_start() because a session is already active in %s on line %d
29+
Notice: session_start(): Ignoring session_start() because a session is already automatically active in %s on line %d
3030
bool(true)
3131
string(%d) "%s"
3232
bool(true)

0 commit comments

Comments
 (0)