Skip to content

Fix GH-16590: UAF in session_encode() #16640

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ PHP 8.4 INTERNALS UPGRADE NOTES
needing to create a zend_string.
- The ext/session/php_session.h doesn't transitively include the
ext/hash/php_hash.h header anymore.
- It is no longer allowed to return out of the PS_ENCODE_LOOP macro.
Instead, you should break out of the loop instead.

i. ext/xml
- Made the expat compatibility wrapper XML_GetCurrentByteIndex return a long
Expand Down
8 changes: 7 additions & 1 deletion ext/session/php_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,13 @@ PHPAPI zend_result php_session_reset_id(void);
zend_ulong num_key; \
zval *struc;

/* Do not use a return statement in `code` because that may leak memory.
* Break out of the loop instead. */
#define PS_ENCODE_LOOP(code) do { \
HashTable *_ht = Z_ARRVAL_P(Z_REFVAL(PS(http_session_vars))); \
zval _zv; \
/* protect against user interference */ \
ZVAL_COPY(&_zv, Z_REFVAL(PS(http_session_vars))); \
HashTable *_ht = Z_ARRVAL(_zv); \
ZEND_HASH_FOREACH_KEY(_ht, num_key, key) { \
if (key == NULL) { \
php_error_docref(NULL, E_WARNING, \
Expand All @@ -303,6 +308,7 @@ PHPAPI zend_result php_session_reset_id(void);
code; \
} \
} ZEND_HASH_FOREACH_END(); \
zval_ptr_dtor(&_zv); \
} while(0)

PHPAPI ZEND_EXTERN_MODULE_GLOBALS(ps)
Expand Down
8 changes: 7 additions & 1 deletion ext/session/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,7 @@ PS_SERIALIZER_ENCODE_FUNC(php) /* {{{ */
{
smart_str buf = {0};
php_serialize_data_t var_hash;
bool fail = false;
PS_ENCODE_VARS;

PHP_VAR_SERIALIZE_INIT(var_hash);
Expand All @@ -1065,12 +1066,17 @@ PS_SERIALIZER_ENCODE_FUNC(php) /* {{{ */
if (memchr(ZSTR_VAL(key), PS_DELIMITER, ZSTR_LEN(key))) {
PHP_VAR_SERIALIZE_DESTROY(var_hash);
smart_str_free(&buf);
return NULL;
fail = true;
break;
}
smart_str_appendc(&buf, PS_DELIMITER);
php_var_serialize(&buf, struc, &var_hash);
);

if (fail) {
return NULL;
}

smart_str_0(&buf);

PHP_VAR_SERIALIZE_DESTROY(var_hash);
Expand Down
36 changes: 36 additions & 0 deletions ext/session/tests/gh16590.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
GH-16590 (UAF in session_encode())
--EXTENSIONS--
session
--SKIPIF--
<?php include('skipif.inc'); ?>
--INI--
session.use_cookies=0
session.cache_limiter=
session.serialize_handler=php
session.save_handler=files
--FILE--
<?php

class C {
function __serialize() {
$_SESSION = [];
return [];
}
}

session_start();

$_SESSION['Lz'] = new C;
for ($i = 0; $i < 2; $i++) {
$_SESSION[$i] = $i;
}

var_dump(session_encode());

?>
--EXPECTF--
Warning: session_encode(): Skipping numeric key 0 in %s on line %d

Warning: session_encode(): Skipping numeric key 1 in %s on line %d
string(15) "Lz|O:1:"C":0:{}"