Skip to content

Commit 86b4921

Browse files
authored
ext/session: only return false when could not encode session at all (php#21181)
* ext/session: only return false when could not encode session at all This also fixes bug 71162
1 parent 0375697 commit 86b4921

8 files changed

Lines changed: 75 additions & 47 deletions

ext/session/session.c

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -507,24 +507,24 @@ static void php_session_save_current_state(bool write)
507507
IF_SESSION_VARS() {
508508
zval *handler_function = &PS(mod_user_names).ps_write;
509509
if (PS(mod_data) || PS(mod_user_implemented)) {
510-
zend_string *val;
511-
512-
val = php_session_encode();
513-
if (val) {
514-
if (PS(lazy_write) && PS(session_vars)
515-
&& PS(mod)->s_update_timestamp
516-
&& PS(mod)->s_update_timestamp != php_session_update_timestamp
517-
&& zend_string_equals(val, PS(session_vars))
518-
) {
519-
ret = PS(mod)->s_update_timestamp(&PS(mod_data), PS(id), val, PS(gc_maxlifetime));
520-
handler_function = &PS(mod_user_names).ps_update_timestamp;
521-
} else {
522-
ret = PS(mod)->s_write(&PS(mod_data), PS(id), val, PS(gc_maxlifetime));
523-
}
524-
zend_string_release_ex(val, false);
510+
zend_string *val = php_session_encode();
511+
/* Not being able to encode the session data means there is some kind of issue that prevents a write
512+
* (e.g. a key containing the '|' character with the default serialization) */
513+
if (UNEXPECTED(val == NULL)) {
514+
return;
515+
}
516+
517+
if (PS(lazy_write) && PS(session_vars)
518+
&& PS(mod)->s_update_timestamp
519+
&& PS(mod)->s_update_timestamp != php_session_update_timestamp
520+
&& zend_string_equals(val, PS(session_vars))
521+
) {
522+
ret = PS(mod)->s_update_timestamp(&PS(mod_data), PS(id), val, PS(gc_maxlifetime));
523+
handler_function = &PS(mod_user_names).ps_update_timestamp;
525524
} else {
526-
ret = PS(mod)->s_write(&PS(mod_data), PS(id), ZSTR_EMPTY_ALLOC(), PS(gc_maxlifetime));
525+
ret = PS(mod)->s_write(&PS(mod_data), PS(id), val, PS(gc_maxlifetime));
527526
}
527+
zend_string_release_ex(val, false);
528528
}
529529

530530
if ((ret == FAILURE) && !EG(exception)) {
@@ -929,7 +929,7 @@ PS_SERIALIZER_ENCODE_FUNC(php_serialize)
929929
php_var_serialize(&buf, Z_REFVAL(PS(http_session_vars)), &var_hash);
930930
PHP_VAR_SERIALIZE_DESTROY(var_hash);
931931
}
932-
return buf.s;
932+
return smart_str_extract(&buf);
933933
}
934934

935935
PS_SERIALIZER_DECODE_FUNC(php_serialize)
@@ -980,11 +980,9 @@ PS_SERIALIZER_ENCODE_FUNC(php_binary)
980980
smart_str_append(&buf, key);
981981
php_var_serialize(&buf, struc, &var_hash);
982982
);
983-
984-
smart_str_0(&buf);
985983
PHP_VAR_SERIALIZE_DESTROY(var_hash);
986984

987-
return buf.s;
985+
return smart_str_extract(&buf);
988986
}
989987

990988
PS_SERIALIZER_DECODE_FUNC(php_binary)
@@ -1038,7 +1036,6 @@ PS_SERIALIZER_ENCODE_FUNC(php)
10381036
PS_ENCODE_LOOP(
10391037
smart_str_append(&buf, key);
10401038
if (memchr(ZSTR_VAL(key), PS_DELIMITER, ZSTR_LEN(key))) {
1041-
PHP_VAR_SERIALIZE_DESTROY(var_hash);
10421039
smart_str_free(&buf);
10431040
fail = true;
10441041
php_error_docref(NULL, E_WARNING, "Failed to write session data. Data contains invalid key \"%s\"", ZSTR_VAL(key));
@@ -1047,15 +1044,15 @@ PS_SERIALIZER_ENCODE_FUNC(php)
10471044
smart_str_appendc(&buf, PS_DELIMITER);
10481045
php_var_serialize(&buf, struc, &var_hash);
10491046
);
1047+
PHP_VAR_SERIALIZE_DESTROY(var_hash);
10501048

10511049
if (fail) {
10521050
return NULL;
10531051
}
10541052

1055-
smart_str_0(&buf);
1053+
zend_string *encoded = smart_str_extract(&buf);
10561054

1057-
PHP_VAR_SERIALIZE_DESTROY(var_hash);
1058-
return buf.s;
1055+
return encoded;
10591056
}
10601057

10611058
PS_SERIALIZER_DECODE_FUNC(php)
@@ -2280,7 +2277,6 @@ PHP_FUNCTION(session_id)
22802277
PHP_FUNCTION(session_regenerate_id)
22812278
{
22822279
bool del_ses = false;
2283-
zend_string *data;
22842280

22852281
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &del_ses) == FAILURE) {
22862282
RETURN_THROWS();
@@ -2307,14 +2303,17 @@ PHP_FUNCTION(session_regenerate_id)
23072303
RETURN_FALSE;
23082304
}
23092305
} else {
2310-
zend_result ret;
2311-
data = php_session_encode();
2312-
if (data) {
2313-
ret = PS(mod)->s_write(&PS(mod_data), PS(id), data, PS(gc_maxlifetime));
2314-
zend_string_release_ex(data, false);
2315-
} else {
2316-
ret = PS(mod)->s_write(&PS(mod_data), PS(id), ZSTR_EMPTY_ALLOC(), PS(gc_maxlifetime));
2306+
zend_string *old_session_data = php_session_encode();
2307+
/* If we have no data we must destroy the related session ID */
2308+
if (UNEXPECTED(old_session_data == NULL)) {
2309+
PS(mod)->s_close(&PS(mod_data));
2310+
PS(session_status) = php_session_none;
2311+
RETURN_FALSE;
23172312
}
2313+
2314+
zend_result ret = PS(mod)->s_write(&PS(mod_data), PS(id), old_session_data, PS(gc_maxlifetime));
2315+
zend_string_release_ex(old_session_data, false);
2316+
23182317
if (ret == FAILURE) {
23192318
PS(mod)->s_close(&PS(mod_data));
23202319
PS(session_status) = php_session_none;
@@ -2368,6 +2367,7 @@ PHP_FUNCTION(session_regenerate_id)
23682367
// TODO warn that ID cannot be verified? else { }
23692368
}
23702369
/* Read is required to make new session data at this point. */
2370+
zend_string *data = NULL;
23712371
if (PS(mod)->s_read(&PS(mod_data), PS(id), &data, PS(gc_maxlifetime)) == FAILURE) {
23722372
PS(mod)->s_close(&PS(mod_data));
23732373
PS(session_status) = php_session_none;

ext/session/tests/session_encode_error2.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,28 +51,28 @@ ob_end_flush();
5151
bool(true)
5252

5353
Warning: session_encode(): Skipping numeric key 0 in %s on line %d
54-
bool(false)
54+
string(0) ""
5555
bool(true)
5656

5757
-- Iteration 2 --
5858
bool(true)
5959

6060
Warning: session_encode(): Skipping numeric key 1 in %s on line %d
61-
bool(false)
61+
string(0) ""
6262
bool(true)
6363

6464
-- Iteration 3 --
6565
bool(true)
6666

6767
Warning: session_encode(): Skipping numeric key 12345 in %s on line %d
68-
bool(false)
68+
string(0) ""
6969
bool(true)
7070

7171
-- Iteration 4 --
7272
bool(true)
7373

7474
Warning: session_encode(): Skipping numeric key -2345 in %s on line %d
75-
bool(false)
75+
string(0) ""
7676
bool(true)
7777

7878
-- Iteration 5 --
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
session_encode(): partial session data
3+
--INI--
4+
serialize_precision=100
5+
--EXTENSIONS--
6+
session
7+
--SKIPIF--
8+
<?php include('skipif.inc'); ?>
9+
--FILE--
10+
<?php
11+
12+
ob_start();
13+
14+
session_start();
15+
16+
$_SESSION['data1'] = 'hello';
17+
$_SESSION['data2'] = 'PHP';
18+
$_SESSION['partial|data'] = 'key with pipe';
19+
$_SESSION['data3'] = 'world';
20+
21+
var_dump(session_encode());
22+
23+
var_dump(session_destroy());
24+
25+
ob_end_flush();
26+
?>
27+
--EXPECTF--
28+
Warning: session_encode(): Failed to write session data. Data contains invalid key "partial|data" in %s on line %d
29+
bool(false)
30+
bool(true)

ext/session/tests/session_encode_variation1.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ ob_end_flush();
3030
Warning: session_encode(): Cannot encode non-existent session in %s on line %d
3131
bool(false)
3232
bool(true)
33-
bool(false)
33+
string(0) ""
3434
bool(true)
35-
bool(false)
35+
string(0) ""
3636
bool(true)
37-
bool(false)
37+
string(0) ""
3838
bool(true)
3939

4040
Warning: session_encode(): Cannot encode non-existent session in %s on line %d

ext/session/tests/session_encode_variation2.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ ob_end_flush();
2222
?>
2323
--EXPECTF--
2424
*** Testing session_encode() : variation ***
25-
bool(false)
25+
string(0) ""
2626
bool(true)
2727

2828
Warning: session_encode(): Cannot encode non-existent session in %s on line %d

ext/session/tests/session_encode_variation6.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,16 @@ ob_end_flush();
3232
bool(true)
3333

3434
Warning: session_encode(): Skipping numeric key 0 in %s on line %d
35-
bool(false)
35+
string(0) ""
3636
bool(true)
3737
bool(true)
3838

3939
Warning: session_encode(): Skipping numeric key 1234567890 in %s on line %d
40-
bool(false)
40+
string(0) ""
4141
bool(true)
4242
bool(true)
4343

4444
Warning: session_encode(): Skipping numeric key -1234567890 in %s on line %d
45-
bool(false)
45+
string(0) ""
4646
bool(true)
4747
Done

ext/session/tests/user_session_module/bug71162.phpt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ updateTimestamp never called when session data is empty
44
session
55
--INI--
66
session.use_strict_mode=0
7-
--XFAIL--
8-
Current session module is designed to write empty session always.
97
--FILE--
108
<?php
119
class MySessionHandler extends SessionHandler implements SessionUpdateTimestampHandlerInterface
@@ -71,7 +69,7 @@ session_commit();
7169
--EXPECT--
7270
string(40) "da39a3ee5e6b4b0d3255bfef95601890afd80709"
7371
bool(true)
74-
write
72+
updateTimestamp
7573
string(40) "da39a3ee5e6b4b0d3255bfef95601890afd80709"
7674
bool(true)
7775
updateTimestamp

ext/session/tests/user_session_module/session_set_save_handler_variation5.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ Read [%s,PHPT-%d]
8383
GC [0]
8484
1 deleted
8585
bool(true)
86-
Write [%s,PHPT-%d,]
86+
Update [%s,PHPT-%d]
8787
Close [%s,PHPSESSID]
8888
bool(true)
8989
string(%d) "PHPT-%d"

0 commit comments

Comments
 (0)