From 925ba18cbdb605523852f895e2589649346abea8 Mon Sep 17 00:00:00 2001 From: Arshid Date: Thu, 25 Jun 2026 21:06:51 +0530 Subject: [PATCH 1/4] Fix GH-22449: Fix NULL dereference in user_filter_factory_create() (#22453) Fix #22449, a NULL pointer dereference in `user_filter_factory_create()` POC: ```php +--EXPECTF-- +done + +Warning: stream_filter_append(): Unable to create or locate filter "rotator_notWorking" in %s on line %d diff --git a/ext/standard/user_filters.c b/ext/standard/user_filters.c index ae4132733bec..986bbbd5f4d3 100644 --- a/ext/standard/user_filters.c +++ b/ext/standard/user_filters.c @@ -337,6 +337,10 @@ static php_stream_filter *user_filter_factory_create(const char *filtername, return NULL; } + if (UNEXPECTED(BG(user_filter_map) == NULL)) { + return NULL; + } + len = strlen(filtername); /* determine the classname/class entry */ From 476fde398834575733a16b92a33a259b2c1ee5c4 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Thu, 25 Jun 2026 18:29:18 +0200 Subject: [PATCH 2/4] Remove includes (#22445) Follow up of dc34d34230ec152abfe27bf492d206a294359320 --- ext/standard/file.c | 6 +----- ext/standard/filestat.c | 6 +----- ext/standard/pageinfo.c | 6 +----- main/php.h | 8 ++------ 4 files changed, 5 insertions(+), 21 deletions(-) diff --git a/ext/standard/file.c b/ext/standard/file.c index f9474af68735..0bb00355744f 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -61,11 +61,7 @@ #include "file.h" #ifdef HAVE_PWD_H -# ifdef PHP_WIN32 -# include "win32/pwd.h" -# else -# include -# endif +# include #endif #include "fsock.h" diff --git a/ext/standard/filestat.c b/ext/standard/filestat.c index e41f6d1abb61..f7bda3c66bab 100644 --- a/ext/standard/filestat.c +++ b/ext/standard/filestat.c @@ -54,11 +54,7 @@ #endif #ifdef HAVE_PWD_H -# ifdef PHP_WIN32 -# include "win32/pwd.h" -# else -# include -# endif +# include #endif #ifdef HAVE_GRP_H diff --git a/ext/standard/pageinfo.c b/ext/standard/pageinfo.c index 3ddda25ef0b7..66ba0da5f158 100644 --- a/ext/standard/pageinfo.c +++ b/ext/standard/pageinfo.c @@ -19,11 +19,7 @@ #include #include #ifdef HAVE_PWD_H -#ifdef PHP_WIN32 -#include "win32/pwd.h" -#else -#include -#endif +# include #endif #ifdef HAVE_GRP_H # include diff --git a/main/php.h b/main/php.h index 44076b2c294d..77976a68c018 100644 --- a/main/php.h +++ b/main/php.h @@ -205,12 +205,8 @@ typedef unsigned int socklen_t; #include #ifdef HAVE_PWD_H -# ifdef PHP_WIN32 -#include "win32/param.h" -# else -#include -#include -# endif +# include +# include #endif #include From cd80e3e807ce8ba90a9335a94f90a39dea0c24cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Thu, 25 Jun 2026 21:26:21 +0200 Subject: [PATCH 3/4] json: Store errors in a structured fashion (#22421) This will make it both easier to consume the error information and reduce the chance for mistakes when settings errors, because not every field will need to be set manually. The functions that have been removed as part of this change were added in master only, thus removing those is not an API change between the latest stable (PHP 8.5) and this PR. Fixes php/php-src#22420. --- ext/json/json.c | 82 +++++++++++++++++-------------------- ext/json/json_parser.y | 11 ++--- ext/json/php_json.h | 16 ++++++-- ext/json/php_json_parser.h | 4 +- ext/json/tests/gh22420.phpt | 16 ++++++++ 5 files changed, 72 insertions(+), 57 deletions(-) create mode 100644 ext/json/tests/gh22420.phpt diff --git a/ext/json/json.c b/ext/json/json.c index 3ba53e959c5d..ac033c057ac4 100644 --- a/ext/json/json.c +++ b/ext/json/json.c @@ -60,18 +60,14 @@ static PHP_GINIT_FUNCTION(json) ZEND_TSRMLS_CACHE_UPDATE(); #endif json_globals->encoder_depth = 0; - json_globals->error_code = 0; - json_globals->error_line = 0; - json_globals->error_column = 0; + php_json_error_details_clear(&json_globals->error_details); json_globals->encode_max_depth = PHP_JSON_PARSER_DEFAULT_DEPTH; } /* }}} */ static PHP_RINIT_FUNCTION(json) { - JSON_G(error_code) = 0; - JSON_G(error_line) = 0; - JSON_G(error_column) = 0; + php_json_error_details_clear(&JSON_G(error_details)); return SUCCESS; } @@ -134,7 +130,11 @@ PHP_JSON_API zend_result php_json_encode_ex(smart_str *buf, zval *val, int optio encoder.max_depth = depth; return_code = php_json_encode_zval(buf, val, options, &encoder); - JSON_G(error_code) = encoder.error_code; + JSON_G(error_details) = (php_json_error_details){ + .code = encoder.error_code, + .line = 0, + .column = 0, + }; return return_code; } @@ -179,12 +179,12 @@ static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{ } /* }}} */ -static zend_string *php_json_get_error_msg_with_location(php_json_error_code error_code, size_t line, size_t column) /* {{{ */ +static zend_string *php_json_get_error_msg_with_location(const php_json_error_details *details) /* {{{ */ { - const char *base_msg = php_json_get_error_msg(error_code); + const char *base_msg = php_json_get_error_msg(details->code); - if (line > 0 && column > 0) { - return zend_strpprintf(0, "%s near location %zu:%zu", base_msg, line, column); + if (details->line > 0 && details->column > 0) { + return zend_strpprintf(0, "%s near location %zu:%zu", base_msg, details->line, details->column); } return zend_string_init(base_msg, strlen(base_msg), 0); @@ -198,17 +198,14 @@ PHP_JSON_API zend_result php_json_decode_ex(zval *return_value, const char *str, php_json_parser_init(&parser, return_value, str, str_len, (int)options, (int)depth); if (php_json_yyparse(&parser)) { - php_json_error_code error_code = php_json_parser_error_code(&parser); - size_t error_line = php_json_parser_error_line(&parser); - size_t error_column = php_json_parser_error_column(&parser); + php_json_error_details details; + php_json_parser_error_details(&parser, &details); if (!(options & PHP_JSON_THROW_ON_ERROR)) { - JSON_G(error_code) = error_code; - JSON_G(error_line) = error_line; - JSON_G(error_column) = error_column; + JSON_G(error_details) = details; } else { - zend_string *error_msg = php_json_get_error_msg_with_location(error_code, error_line, error_column); - zend_throw_exception(php_json_exception_ce, ZSTR_VAL(error_msg), error_code); + zend_string *error_msg = php_json_get_error_msg_with_location(&details); + zend_throw_exception(php_json_exception_ce, ZSTR_VAL(error_msg), details.code); zend_string_release(error_msg); } RETVAL_NULL(); @@ -228,13 +225,8 @@ PHP_JSON_API bool php_json_validate_ex(const char *str, size_t str_len, zend_lon php_json_parser_init_ex(&parser, &tmp, str, str_len, (int)options, (int)depth, parser_validate_methods); if (php_json_yyparse(&parser)) { - php_json_error_code error_code = php_json_parser_error_code(&parser); - size_t error_line = php_json_parser_error_line(&parser); - size_t error_column = php_json_parser_error_column(&parser); + php_json_parser_error_details(&parser, &JSON_G(error_details)); - JSON_G(error_code) = error_code; - JSON_G(error_line) = error_line; - JSON_G(error_column) = error_column; return false; } @@ -263,7 +255,12 @@ PHP_FUNCTION(json_encode) php_json_encode_zval(&buf, parameter, (int)options, &encoder); if (!(options & PHP_JSON_THROW_ON_ERROR) || (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { - JSON_G(error_code) = encoder.error_code; + JSON_G(error_details) = (php_json_error_details){ + .code = encoder.error_code, + .line = 0, + .column = 0, + }; + if (encoder.error_code != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { smart_str_free(&buf); RETURN_FALSE; @@ -299,16 +296,16 @@ PHP_FUNCTION(json_decode) ZEND_PARSE_PARAMETERS_END(); if (!(options & PHP_JSON_THROW_ON_ERROR)) { - JSON_G(error_code) = PHP_JSON_ERROR_NONE; - JSON_G(error_line) = 0; - JSON_G(error_column) = 0; + php_json_error_details_clear(&JSON_G(error_details)); } if (!str_len) { if (!(options & PHP_JSON_THROW_ON_ERROR)) { - JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX; - JSON_G(error_line) = 0; - JSON_G(error_column) = 0; + JSON_G(error_details) = (php_json_error_details){ + .code = PHP_JSON_ERROR_SYNTAX, + .line = 0, + .column = 0, + }; } else { zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(PHP_JSON_ERROR_SYNTAX), PHP_JSON_ERROR_SYNTAX); } @@ -360,15 +357,16 @@ PHP_FUNCTION(json_validate) } if (!str_len) { - JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX; - JSON_G(error_line) = 0; - JSON_G(error_column) = 0; + JSON_G(error_details) = (php_json_error_details){ + .code = PHP_JSON_ERROR_SYNTAX, + .line = 0, + .column = 0, + }; + RETURN_FALSE; } - JSON_G(error_code) = PHP_JSON_ERROR_NONE; - JSON_G(error_line) = 0; - JSON_G(error_column) = 0; + php_json_error_details_clear(&JSON_G(error_details)); if (depth <= 0) { zend_argument_value_error(2, "must be greater than 0"); @@ -389,7 +387,7 @@ PHP_FUNCTION(json_last_error) { ZEND_PARSE_PARAMETERS_NONE(); - RETURN_LONG(JSON_G(error_code)); + RETURN_LONG(JSON_G(error_details).code); } /* }}} */ @@ -398,10 +396,6 @@ PHP_FUNCTION(json_last_error_msg) { ZEND_PARSE_PARAMETERS_NONE(); - RETVAL_STR(php_json_get_error_msg_with_location( - JSON_G(error_code), - JSON_G(error_line), - JSON_G(error_column) - )); + RETURN_STR(php_json_get_error_msg_with_location(&JSON_G(error_details))); } /* }}} */ diff --git a/ext/json/json_parser.y b/ext/json/json_parser.y index cf296ed9db62..0d3b90b29e1e 100644 --- a/ext/json/json_parser.y +++ b/ext/json/json_parser.y @@ -311,14 +311,11 @@ PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parse return parser->scanner.errcode; } -PHP_JSON_API size_t php_json_parser_error_line(const php_json_parser *parser) +PHP_JSON_API void php_json_parser_error_details(const php_json_parser *parser, php_json_error_details *out) { - return parser->scanner.errloc.first_line; -} - -PHP_JSON_API size_t php_json_parser_error_column(const php_json_parser *parser) -{ - return parser->scanner.errloc.first_column; + out->code = parser->scanner.errcode; + out->line = parser->scanner.errloc.first_line; + out->column = parser->scanner.errloc.first_column; } static const php_json_parser_methods default_parser_methods = diff --git a/ext/json/php_json.h b/ext/json/php_json.h index e2728835ed42..f34684e149d8 100644 --- a/ext/json/php_json.h +++ b/ext/json/php_json.h @@ -52,6 +52,18 @@ typedef enum { PHP_JSON_ERROR_NON_BACKED_ENUM, } php_json_error_code; +typedef struct php_json_error_details { + php_json_error_code code; + size_t line; + size_t column; +} php_json_error_details; + +static inline void php_json_error_details_clear(php_json_error_details *out) { + out->code = PHP_JSON_ERROR_NONE; + out->line = 0; + out->column = 0; +} + /* json_decode() options */ #define PHP_JSON_OBJECT_AS_ARRAY (1<<0) #define PHP_JSON_BIGINT_AS_STRING (1<<1) @@ -83,9 +95,7 @@ typedef enum { ZEND_BEGIN_MODULE_GLOBALS(json) int encoder_depth; int encode_max_depth; - php_json_error_code error_code; - size_t error_line; - size_t error_column; + php_json_error_details error_details; ZEND_END_MODULE_GLOBALS(json) PHP_JSON_API ZEND_EXTERN_MODULE_GLOBALS(json) diff --git a/ext/json/php_json_parser.h b/ext/json/php_json_parser.h index 4a7d64307c43..888a0d317fe0 100644 --- a/ext/json/php_json_parser.h +++ b/ext/json/php_json_parser.h @@ -83,9 +83,7 @@ PHP_JSON_API void php_json_parser_init( PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser); -PHP_JSON_API size_t php_json_parser_error_line(const php_json_parser *parser); - -PHP_JSON_API size_t php_json_parser_error_column(const php_json_parser *parser); +PHP_JSON_API void php_json_parser_error_details(const php_json_parser *parser, php_json_error_details *out); PHP_JSON_API int php_json_parse(php_json_parser *parser); diff --git a/ext/json/tests/gh22420.phpt b/ext/json/tests/gh22420.phpt new file mode 100644 index 000000000000..eecdc6d97a8d --- /dev/null +++ b/ext/json/tests/gh22420.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-22420: json_encode() errors don't clear the error position +--FILE-- + +--EXPECTF-- +NULL +string(30) "Syntax error near location 1:2" +bool(false) +string(56) "Malformed UTF-8 characters, possibly incorrectly encoded" From 6d98178ca39213349c441fe7fa2341bcf9e64845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 25 Jun 2026 21:57:38 +0200 Subject: [PATCH 4/4] Improve Uri\Rfc3986\Builder tests Addressing a few late review comments --- ext/uri/tests/rfc3986/builder/host_success_null.phpt | 2 ++ ext/uri/tests/rfc3986/builder/path_success_empty_string.phpt | 1 + 2 files changed, 3 insertions(+) diff --git a/ext/uri/tests/rfc3986/builder/host_success_null.phpt b/ext/uri/tests/rfc3986/builder/host_success_null.phpt index 87217d0ef285..f1689749866b 100644 --- a/ext/uri/tests/rfc3986/builder/host_success_null.phpt +++ b/ext/uri/tests/rfc3986/builder/host_success_null.phpt @@ -10,6 +10,7 @@ $uri = $builder->build(); var_dump($uri->toRawString()); var_dump($uri); +var_dump($uri->equals(new Uri\Rfc3986\Uri($uri->toRawString()))); ?> --EXPECTF-- @@ -32,3 +33,4 @@ object(Uri\Rfc3986\Uri)#%d (%d) { ["fragment"]=> NULL } +bool(true) diff --git a/ext/uri/tests/rfc3986/builder/path_success_empty_string.phpt b/ext/uri/tests/rfc3986/builder/path_success_empty_string.phpt index 00ed0c953550..78ea6cf12cd3 100644 --- a/ext/uri/tests/rfc3986/builder/path_success_empty_string.phpt +++ b/ext/uri/tests/rfc3986/builder/path_success_empty_string.phpt @@ -4,6 +4,7 @@ Test Uri\Rfc3986\UriBuilder::setPath() - success - empty string setPath("/foo/bar"); $builder->setPath(""); $uri = $builder->build();