From 60ddc8fbf1185fe82d04c91c986b548dbb578478 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 25 Sep 2026 15:33:15 -0700 Subject: [PATCH 1/2] JSON fuzzer: use options for handling invalid UTF-8 and big strings (#23877) Previously, the fuzzer only used the default options (`0`) and the `PHP_JSON_OBJECT_AS_ARRAY` option (`1`). After running the fuzzer on its existing corpus with `-reduce_inputs=0`, `-runs=100000`, and `-seed=1`, it failed to reach the code paths for handling big integers and strings, or for dealing with invalid UTF8, within the allotted 100,000 runs. Those 100,000 runs resulted in coverage of roughly 2,020 code blocks or edges, and roughly 5,400 "features". Expand the fuzzer to also run with options that include the `PHP_JSON_BIGINT_AS_STRING`, `PHP_JSON_INVALID_UTF8_IGNORE`, and `PHP_JSON_INVALID_UTF8_SUBSTITUTE` flags. The two flags for handling UTF-8 are not applied used together, since invalid UTF-8 can only be handled one way, but other than that all combinations of these flags and `PHP_JSON_OBJECT_AS_ARRAY` are now tested. Repeating the same fuzzing as earlier with the expanded options results in roughly 2,150 code blocks or edges, and roughly 5,700 "features", being covered. --- sapi/fuzzer/fuzzer-json.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/sapi/fuzzer/fuzzer-json.c b/sapi/fuzzer/fuzzer-json.c index 5029cb9a585d..0407a895835e 100644 --- a/sapi/fuzzer/fuzzer-json.c +++ b/sapi/fuzzer/fuzzer-json.c @@ -24,6 +24,7 @@ #include #include "fuzzer-sapi.h" +#include "ext/json/php_json.h" #include "ext/json/php_json_parser.h" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { @@ -36,10 +37,25 @@ int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { memcpy(data, Data, Size); data[Size] = '\0'; - for (int option = 0; option <=1; ++option) { + int options[12] = { + 0, + PHP_JSON_OBJECT_AS_ARRAY, + PHP_JSON_OBJECT_AS_ARRAY | PHP_JSON_BIGINT_AS_STRING, + PHP_JSON_OBJECT_AS_ARRAY | PHP_JSON_BIGINT_AS_STRING | PHP_JSON_INVALID_UTF8_IGNORE, + PHP_JSON_OBJECT_AS_ARRAY | PHP_JSON_BIGINT_AS_STRING | PHP_JSON_INVALID_UTF8_SUBSTITUTE, + PHP_JSON_OBJECT_AS_ARRAY | PHP_JSON_INVALID_UTF8_IGNORE, + PHP_JSON_OBJECT_AS_ARRAY | PHP_JSON_INVALID_UTF8_SUBSTITUTE, + PHP_JSON_BIGINT_AS_STRING, + PHP_JSON_BIGINT_AS_STRING | PHP_JSON_INVALID_UTF8_IGNORE, + PHP_JSON_BIGINT_AS_STRING | PHP_JSON_INVALID_UTF8_SUBSTITUTE, + PHP_JSON_INVALID_UTF8_IGNORE, + PHP_JSON_INVALID_UTF8_SUBSTITUTE + }; + + for (int index = 0; index < 12; ++index) { zval result; php_json_parser parser; - php_json_parser_init(&parser, &result, data, Size, option, 10); + php_json_parser_init(&parser, &result, data, Size, options[index], 10); if (php_json_yyparse(&parser) == SUCCESS) { zval_ptr_dtor(&result); } From 814ade88806a4865cbdc517fc164e7941dd53dd3 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 26 Sep 2026 00:57:18 +0100 Subject: [PATCH 2/2] session: clean-up code relating to calling user functions (#23909) --- ext/session/mod_user.c | 70 ++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 44 deletions(-) diff --git a/ext/session/mod_user.c b/ext/session/mod_user.c index a023ef7e4280..0ac08f92f969 100644 --- a/ext/session/mod_user.c +++ b/ext/session/mod_user.c @@ -22,9 +22,8 @@ const ps_module ps_mod_user = { PS_MOD_UPDATE_TIMESTAMP(user) }; -static void ps_call_handler(zval *func, int argc, zval *argv, zval *retval) +static void ps_call_handler(zval *func, uint32_t argc, zval *argv, zval *retval) { - int i; if (PS(in_save_handler)) { PS(in_save_handler) = false; ZVAL_UNDEF(retval); @@ -34,12 +33,10 @@ static void ps_call_handler(zval *func, int argc, zval *argv, zval *retval) if (call_user_function(NULL, NULL, func, retval, argc, argv) == FAILURE) { zval_ptr_dtor(retval); ZVAL_UNDEF(retval); - } else if (Z_ISUNDEF_P(retval)) { - ZVAL_NULL(retval); } PS(in_save_handler) = false; } - for (i = 0; i < argc; i++) { + for (uint32_t i = 0; i < argc; i++) { zval_ptr_dtor(&argv[i]); } } @@ -48,34 +45,27 @@ static void ps_call_handler(zval *func, int argc, zval *argv, zval *retval) static zend_result verify_bool_return_type_userland_calls(const zval *value) { - /* Exit or exception in userland call */ - if (Z_TYPE_P(value) == IS_UNDEF) { - return FAILURE; - } - if (Z_TYPE_P(value) == IS_TRUE) { - return SUCCESS; - } - if (Z_TYPE_P(value) == IS_FALSE) { - return FAILURE; - } - if ((Z_TYPE_P(value) == IS_LONG) && (Z_LVAL_P(value) == -1)) { - /* TODO Why are exceptions checked? */ - if (!EG(exception)) { - php_error_docref(NULL, E_DEPRECATED, "Session callback must have a return value of type bool, %s returned", zend_zval_value_name(value)); - } - return FAILURE; - } - if ((Z_TYPE_P(value) == IS_LONG) && (Z_LVAL_P(value) == 0)) { - /* TODO Why are exceptions checked? */ - if (!EG(exception)) { - php_error_docref(NULL, E_DEPRECATED, "Session callback must have a return value of type bool, %s returned", zend_zval_value_name(value)); - } - return SUCCESS; - } - if (!EG(exception)) { - zend_type_error("Session callback must have a return value of type bool, %s returned", zend_zval_value_name(value)); \ + switch (Z_TYPE_P(value)) { + case IS_TRUE: + return SUCCESS; + case IS_FALSE: + /* Exit or exception in userland call */ + case IS_UNDEF: + return FAILURE; + case IS_LONG: + /* Deprecated cases */ + if (Z_LVAL_P(value) == 0) { + php_error_docref(NULL, E_DEPRECATED, "Session callback must have a return value of type bool, %s returned", zend_zval_value_name(value)); + return SUCCESS; + } else if (Z_LVAL_P(value) == -1) { + php_error_docref(NULL, E_DEPRECATED, "Session callback must have a return value of type bool, %s returned", zend_zval_value_name(value)); + return FAILURE; + } + ZEND_FALLTHROUGH; + default: + zend_type_error("Session callback must have a return value of type bool, %s returned", zend_zval_value_name(value)); + return FAILURE; } - return FAILURE; } PS_OPEN_FUNC(user) @@ -92,9 +82,7 @@ PS_OPEN_FUNC(user) ps_call_handler(&PSF(open), 2, args, &retval); } zend_catch { PS(session_status) = php_session_none; - if (!Z_ISUNDEF(retval)) { - zval_ptr_dtor(&retval); - } + zval_ptr_dtor(&retval); zend_bailout(); } zend_end_try(); @@ -107,7 +95,6 @@ PS_OPEN_FUNC(user) PS_CLOSE_FUNC(user) { - bool bailout = false; zval retval; zend_result ret = FAILURE; @@ -121,18 +108,13 @@ PS_CLOSE_FUNC(user) zend_try { ps_call_handler(&PSF(close), 0, NULL, &retval); } zend_catch { - bailout = true; + PS(mod_user_implemented) = false; + zval_ptr_dtor(&retval); + zend_bailout(); } zend_end_try(); PS(mod_user_implemented) = false; - if (bailout) { - if (!Z_ISUNDEF(retval)) { - zval_ptr_dtor(&retval); - } - zend_bailout(); - } - ret = verify_bool_return_type_userland_calls(&retval); zval_ptr_dtor(&retval); return ret;