Skip to content
Open
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
82 changes: 38 additions & 44 deletions ext/json/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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();
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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");
Expand All @@ -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);
}
/* }}} */

Expand All @@ -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)));
}
/* }}} */
11 changes: 4 additions & 7 deletions ext/json/json_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
16 changes: 13 additions & 3 deletions ext/json/php_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment thread
TimWolla marked this conversation as resolved.
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)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 1 addition & 3 deletions ext/json/php_json_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
16 changes: 16 additions & 0 deletions ext/json/tests/gh22420.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
GH-22420: json_encode() errors don't clear the error position
--FILE--
<?php

var_dump(json_decode('{132'));
var_dump(json_last_error_msg());
var_dump(json_encode("\xFE\xD0"));
var_dump(json_last_error_msg());

?>
--EXPECTF--
NULL
string(30) "Syntax error near location 1:2"
bool(false)
string(56) "Malformed UTF-8 characters, possibly incorrectly encoded"
Loading