diff options
38 files changed, 1246 insertions, 586 deletions
diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index ed7575d256..1d04646f07 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -1503,21 +1503,21 @@ static void *zend_mm_realloc_heap(zend_mm_heap *heap, void *ptr, size_t size, si ZEND_MM_CHECK(chunk->heap == heap, "zend_mm_heap corrupted"); if (info & ZEND_MM_IS_SRUN) { - int old_bin_num, bin_num; - - old_bin_num = ZEND_MM_SRUN_BIN_NUM(info); + int old_bin_num = ZEND_MM_SRUN_BIN_NUM(info); old_size = bin_data_size[old_bin_num]; - bin_num = ZEND_MM_SMALL_SIZE_TO_BIN(size); - if (old_bin_num == bin_num) { + if (size <= ZEND_MM_MAX_SMALL_SIZE) { + int bin_num = ZEND_MM_SMALL_SIZE_TO_BIN(size); + if (old_bin_num == bin_num) { #if ZEND_DEBUG - dbg = zend_mm_get_debug_info(heap, ptr); - dbg->size = real_size; - dbg->filename = __zend_filename; - dbg->orig_filename = __zend_orig_filename; - dbg->lineno = __zend_lineno; - dbg->orig_lineno = __zend_orig_lineno; + dbg = zend_mm_get_debug_info(heap, ptr); + dbg->size = real_size; + dbg->filename = __zend_filename; + dbg->orig_filename = __zend_orig_filename; + dbg->lineno = __zend_lineno; + dbg->orig_lineno = __zend_orig_lineno; #endif - return ptr; + return ptr; + } } } else /* if (info & ZEND_MM_IS_LARGE_RUN) */ { ZEND_MM_CHECK(ZEND_MM_ALIGNED_OFFSET(page_offset, ZEND_MM_PAGE_SIZE) == 0, "zend_mm_heap corrupted"); diff --git a/ext/bz2/bz2.c b/ext/bz2/bz2.c index 6678876c96..81615e3080 100644 --- a/ext/bz2/bz2.c +++ b/ext/bz2/bz2.c @@ -595,16 +595,26 @@ static PHP_FUNCTION(bzdecompress) /* compression is better then 2:1, need to allocate more memory */ bzs.avail_out = source_len; size = (bzs.total_out_hi32 * (unsigned int) -1) + bzs.total_out_lo32; + if (size > SIZE_MAX) { + /* no reason to continue if we're going to drop it anyway */ + break; + } dest = safe_erealloc(dest, 1, bzs.avail_out+1, (size_t) size ); bzs.next_out = dest + size; } if (error == BZ_STREAM_END || error == BZ_OK) { size = (bzs.total_out_hi32 * (unsigned int) -1) + bzs.total_out_lo32; - dest = safe_erealloc(dest, 1, (size_t) size, 1); - dest[size] = '\0'; - RETVAL_STRINGL(dest, (int) size); - efree(dest); + if (size > SIZE_MAX) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Decompressed size too big, max is %zd", SIZE_MAX); + efree(dest); + RETVAL_LONG(BZ_MEM_ERROR); + } else { + dest = safe_erealloc(dest, 1, (size_t) size, 1); + dest[size] = '\0'; + RETVAL_STRINGL(dest, (size_t) size); + efree(dest); + } } else { /* real error */ efree(dest); RETVAL_LONG(error); diff --git a/ext/curl/interface.c b/ext/curl/interface.c index a20d60ca36..08846808fc 100644 --- a/ext/curl/interface.c +++ b/ext/curl/interface.c @@ -3530,7 +3530,7 @@ PHP_FUNCTION(curl_reset) PHP_FUNCTION(curl_escape) { char *str = NULL, *res = NULL; - size_t str_len = 0; + size_t str_len = 0; zval *zid; php_curl *ch; @@ -3542,6 +3542,10 @@ PHP_FUNCTION(curl_escape) RETURN_FALSE; } + if (ZEND_SIZE_T_INT_OVFL(str_len)) { + RETURN_FALSE; + } + if ((res = curl_easy_escape(ch->cp, str, str_len))) { RETVAL_STRING(res); curl_free(res); @@ -3569,7 +3573,7 @@ PHP_FUNCTION(curl_unescape) RETURN_FALSE; } - if (str_len > INT_MAX) { + if (ZEND_SIZE_T_INT_OVFL(str_len)) { RETURN_FALSE; } diff --git a/ext/exif/exif.c b/ext/exif/exif.c index f62f77a60f..91aeb7dc7b 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -3758,8 +3758,11 @@ static int exif_process_IFD_in_TIFF(image_info_type *ImageInfo, size_t dir_offse fgot = php_stream_read(ImageInfo->infile, ImageInfo->Thumbnail.data, ImageInfo->Thumbnail.size); if (fgot < ImageInfo->Thumbnail.size) { EXIF_ERRLOG_THUMBEOF(ImageInfo) + efree(ImageInfo->Thumbnail.data); + ImageInfo->Thumbnail.data = NULL; + } else { + exif_thumbnail_build(ImageInfo); } - exif_thumbnail_build(ImageInfo); } #ifdef EXIF_DEBUG exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "Read next IFD (THUMBNAIL) done"); diff --git a/ext/exif/tests/bug72627.phpt b/ext/exif/tests/bug72627.phpt new file mode 100644 index 0000000000..bb6a1fae99 --- /dev/null +++ b/ext/exif/tests/bug72627.phpt @@ -0,0 +1,71 @@ +--TEST-- +Bug #72627 (Memory Leakage In exif_process_IFD_in_TIFF) +--SKIPIF-- +<?php if (!extension_loaded('exif')) print 'skip exif extension not available';?> +--FILE-- +<?php + $exif = exif_read_data(__DIR__ . '/bug72627.tiff',0,0,true); + var_dump($exif); +?> +--EXPECTF-- +Warning: exif_read_data(%s): Thumbnail goes IFD boundary or end of file reached in %sbug72627.php on line %d + +Warning: exif_read_data(%s): Error in TIFF: filesize(x04E2) less than start of IFD dir(x829A0004) in %sbug72627.php on line %d + +Warning: exif_read_data(%s): Thumbnail goes IFD boundary or end of file reached in %sbug72627.php on line %d +array(11) { + ["FileName"]=> + string(13) "bug72627.tiff" + ["FileDateTime"]=> + int(%d) + ["FileSize"]=> + int(1250) + ["FileType"]=> + int(7) + ["MimeType"]=> + string(10) "image/tiff" + ["SectionsFound"]=> + string(30) "ANY_TAG, IFD0, THUMBNAIL, EXIF" + ["COMPUTED"]=> + array(10) { + ["html"]=> + string(24) "width="128" height="132"" + ["Height"]=> + int(132) + ["Width"]=> + int(128) + ["IsColor"]=> + int(0) + ["ByteOrderMotorola"]=> + int(0) + ["ApertureFNumber"]=> + string(5) "f/1.0" + ["Thumbnail.FileType"]=> + int(2) + ["Thumbnail.MimeType"]=> + string(10) "image/jpeg" + ["Thumbnail.Height"]=> + int(132) + ["Thumbnail.Width"]=> + int(128) + } + ["XResolution"]=> + string(21) "1414812756/1414812756" + ["THUMBNAIL"]=> + array(5) { + ["ImageWidth"]=> + int(128) + ["ImageLength"]=> + int(132) + ["JPEGInterchangeFormat"]=> + int(1280) + ["JPEGInterchangeFormatLength"]=> + int(100) + ["THUMBNAIL"]=> + NULL + } + ["ExposureTime"]=> + string(21) "1414812756/1414812756" + ["FNumber"]=> + string(21) "1414812756/1414812756" +} diff --git a/ext/exif/tests/bug72627.tiff b/ext/exif/tests/bug72627.tiff Binary files differnew file mode 100644 index 0000000000..229190a604 --- /dev/null +++ b/ext/exif/tests/bug72627.tiff diff --git a/ext/gd/gd.c b/ext/gd/gd.c index d0eea0bd0b..d2655a6022 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -1537,11 +1537,11 @@ PHP_FUNCTION(imagetruecolortopalette) RETURN_FALSE; } - if (ncolors <= 0) { - php_error_docref(NULL, E_WARNING, "Number of colors has to be greater than zero"); + if (ncolors <= 0 || ZEND_LONG_INT_OVFL(ncolors)) { + php_error_docref(NULL, E_WARNING, "Number of colors has to be greater than zero and no more than %d", INT_MAX); RETURN_FALSE; } - gdImageTrueColorToPalette(im, dither, ncolors); + gdImageTrueColorToPalette(im, dither, (int)ncolors); RETURN_TRUE; } @@ -3039,6 +3039,11 @@ PHP_FUNCTION(imagegammacorrect) return; } + if ( input <= 0.0 || output <= 0.0 ) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Gamma values should be positive"); + RETURN_FALSE; + } + if ((im = (gdImagePtr)zend_fetch_resource(Z_RES_P(IM), "Image", le_gd)) == NULL) { RETURN_FALSE; } diff --git a/ext/gd/tests/bug72697.phpt b/ext/gd/tests/bug72697.phpt new file mode 100644 index 0000000000..6110385fcb --- /dev/null +++ b/ext/gd/tests/bug72697.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #72697: select_colors write out-of-bounds +--SKIPIF-- +<?php +if (!function_exists("imagecreatetruecolor")) die("skip"); +if (PHP_INT_MAX !== 9223372036854775807) die("skip for 64-bit long systems only"); +?> +--FILE-- +<?php + +$img=imagecreatetruecolor(10, 10); +imagetruecolortopalette($img, false, PHP_INT_MAX / 8); +?> +DONE +--EXPECTF-- +Warning: imagetruecolortopalette(): Number of colors has to be greater than zero and no more than 2147483647 in %sbug72697.php on line %d +DONE
\ No newline at end of file diff --git a/ext/gd/tests/bug72730.phpt b/ext/gd/tests/bug72730.phpt new file mode 100644 index 0000000000..e7c13cb5e9 --- /dev/null +++ b/ext/gd/tests/bug72730.phpt @@ -0,0 +1,15 @@ +--TEST-- +Bug #72730: imagegammacorrect allows arbitrary write access +--SKIPIF-- +<?php +if (!function_exists("imagecreatetruecolor")) die("skip"); +?> +--FILE-- +<?php +$img = imagecreatetruecolor(1, 1); +imagegammacorrect($img, -1, 1337); +?> +DONE +--EXPECTF-- +Warning: imagegammacorrect(): Gamma values should be positive in %sbug72730.php on line %d +DONE
\ No newline at end of file diff --git a/ext/gd/tests/imagetruecolortopalette_error3.phpt b/ext/gd/tests/imagetruecolortopalette_error3.phpt index de3a62d0a5..46e3f487e0 100644 --- a/ext/gd/tests/imagetruecolortopalette_error3.phpt +++ b/ext/gd/tests/imagetruecolortopalette_error3.phpt @@ -25,4 +25,4 @@ Warning: imagetruecolortopalette() expects parameter 3 to be integer, resource g Warning: imagetruecolortopalette() expects parameter 3 to be integer, array given in %s on line %d -Warning: imagetruecolortopalette(): Number of colors has to be greater than zero in %s on line %d
\ No newline at end of file +Warning: imagetruecolortopalette(): Number of colors has to be greater than zero and no more than %d in %s on line %d diff --git a/ext/gd/tests/imagetruecolortopalette_error4.phpt b/ext/gd/tests/imagetruecolortopalette_error4.phpt index b9661e3b85..f0e18b86be 100644 --- a/ext/gd/tests/imagetruecolortopalette_error4.phpt +++ b/ext/gd/tests/imagetruecolortopalette_error4.phpt @@ -16,6 +16,6 @@ imagetruecolortopalette($image, true, -1); ?> --EXPECTF-- -Warning: imagetruecolortopalette(): Number of colors has to be greater than zero in %s on line %d +Warning: imagetruecolortopalette(): Number of colors has to be greater than zero and no more than %d in %s line %d -Warning: imagetruecolortopalette(): Number of colors has to be greater than zero in %s on line %d
\ No newline at end of file +Warning: imagetruecolortopalette(): Number of colors has to be greater than zero and no more than %d in %s line %d diff --git a/ext/mcrypt/mcrypt.c b/ext/mcrypt/mcrypt.c index cf9c2ccdf1..c2c60c2b3c 100644 --- a/ext/mcrypt/mcrypt.c +++ b/ext/mcrypt/mcrypt.c @@ -633,6 +633,10 @@ PHP_FUNCTION(mcrypt_generic) RETURN_FALSE } + if (data_len > INT_MAX) { + php_error_docref(NULL, E_WARNING, "Data size too large, %d maximum", INT_MAX); + RETURN_FALSE; + } /* Check blocksize */ if (mcrypt_enc_is_block_mode(pm->td) == 1) { /* It's a block algorithm */ block_size = mcrypt_enc_get_block_size(pm->td); @@ -645,10 +649,6 @@ PHP_FUNCTION(mcrypt_generic) memset(ZSTR_VAL(data_str), 0, data_size); memcpy(ZSTR_VAL(data_str), data, data_len); } else { /* It's not a block algorithm */ - if (data_len > INT_MAX) { - php_error_docref(NULL, E_WARNING, "Data size too large, %d maximum", INT_MAX); - RETURN_FALSE; - } data_size = (int)data_len; data_str = zend_string_alloc(data_size, 0); memset(ZSTR_VAL(data_str), 0, data_size); @@ -688,6 +688,10 @@ PHP_FUNCTION(mdecrypt_generic) } /* Check blocksize */ + if (data_len > INT_MAX) { + php_error_docref(NULL, E_WARNING, "Data size too large, %d maximum", INT_MAX); + RETURN_FALSE; + } if (mcrypt_enc_is_block_mode(pm->td) == 1) { /* It's a block algorithm */ block_size = mcrypt_enc_get_block_size(pm->td); data_size = ((((int)data_len - 1) / block_size) + 1) * block_size; @@ -699,10 +703,6 @@ PHP_FUNCTION(mdecrypt_generic) memset(data_s, 0, data_size); memcpy(data_s, data, data_len); } else { /* It's not a block algorithm */ - if (data_len > INT_MAX) { - php_error_docref(NULL, E_WARNING, "Data size too large, %d maximum", INT_MAX); - RETURN_FALSE; - } data_size = (int)data_len; data_s = emalloc(data_size + 1); memset(data_s, 0, data_size); diff --git a/ext/session/session.c b/ext/session/session.c index 1247a99804..b303b90653 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -765,12 +765,19 @@ PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */ const char *endptr = val + vallen; zval session_vars; php_unserialize_data_t var_hash; + int result; zend_string *var_name = zend_string_init("_SESSION", sizeof("_SESSION") - 1, 0); ZVAL_NULL(&session_vars); PHP_VAR_UNSERIALIZE_INIT(var_hash); - php_var_unserialize(&session_vars, (const unsigned char **)&val, (const unsigned char *)endptr, &var_hash); + result = php_var_unserialize( + &session_vars, (const unsigned char **)&val, (const unsigned char *)endptr, &var_hash); PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if (!result) { + zval_ptr_dtor(&session_vars); + ZVAL_NULL(&session_vars); + } + if (!Z_ISUNDEF(PS(http_session_vars))) { zval_ptr_dtor(&PS(http_session_vars)); } @@ -823,11 +830,13 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */ int namelen; zend_string *name; php_unserialize_data_t var_hash; + int skip = 0; PHP_VAR_UNSERIALIZE_INIT(var_hash); for (p = val; p < endptr; ) { zval *tmp; + skip = 0; namelen = ((unsigned char)(*p)) & (~PS_BIN_UNDEF); if (namelen < 0 || namelen > PS_BIN_MAX || (p + namelen) >= endptr) { @@ -844,8 +853,7 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */ if ((tmp = zend_hash_find(&EG(symbol_table), name))) { if ((Z_TYPE_P(tmp) == IS_ARRAY && Z_ARRVAL_P(tmp) == &EG(symbol_table)) || tmp == &PS(http_session_vars)) { - zend_string_release(name); - continue; + skip = 1; } } @@ -854,7 +862,9 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */ current = var_tmp_var(&var_hash); if (php_var_unserialize(current, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash)) { ZVAL_PTR(&rv, current); - php_set_session_var(name, &rv, &var_hash ); + if (!skip) { + php_set_session_var(name, &rv, &var_hash); + } } else { zend_string_release(name); php_session_normalize_vars(); @@ -916,6 +926,7 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */ zend_string *name; int has_value, retval = SUCCESS; php_unserialize_data_t var_hash; + int skip = 0; PHP_VAR_UNSERIALIZE_INIT(var_hash); @@ -924,6 +935,7 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */ while (p < endptr) { zval *tmp; q = p; + skip = 0; while (*q != PS_DELIMITER) { if (++q >= endptr) goto break_outer_loop; } @@ -941,7 +953,7 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */ if ((tmp = zend_hash_find(&EG(symbol_table), name))) { if ((Z_TYPE_P(tmp) == IS_ARRAY && Z_ARRVAL_P(tmp) == &EG(symbol_table)) || tmp == &PS(http_session_vars)) { - goto skip; + skip = 1; } } @@ -950,16 +962,19 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */ current = var_tmp_var(&var_hash); if (php_var_unserialize(current, (const unsigned char **)&q, (const unsigned char *)endptr, &var_hash)) { ZVAL_PTR(&rv, current); - php_set_session_var(name, &rv, &var_hash); + if (!skip) { + php_set_session_var(name, &rv, &var_hash); + } } else { zend_string_release(name); retval = FAILURE; goto break_outer_loop; } } else { - PS_ADD_VARL(name); + if(!skip) { + PS_ADD_VARL(name); + } } -skip: zend_string_release(name); p = q; diff --git a/ext/session/tests/bug72681.phpt b/ext/session/tests/bug72681.phpt new file mode 100644 index 0000000000..ceca6ecc33 --- /dev/null +++ b/ext/session/tests/bug72681.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #72681: PHP Session Data Injection Vulnerability +--SKIPIF-- +<?php include('skipif.inc'); ?> +--FILE-- +<?php +ini_set('session.serialize_handler', 'php'); +session_start(); +$GLOBALS['ryat'] = $GLOBALS; +$_SESSION['ryat'] = 'ryat|O:8:"stdClass":0:{}'; +session_write_close(); +session_start(); +var_dump($_SESSION); +?> +--EXPECT-- +array(0) { +} diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index b32b845c73..cabfa9e7df 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -1008,7 +1008,7 @@ static int php_snmp_parse_oid(zval *object, int st, struct objid_query *objid_qu php_error_docref(NULL, E_WARNING, "Got empty OID array"); return FALSE; } - objid_query->vars = (snmpobjarg *)emalloc(sizeof(snmpobjarg) * zend_hash_num_elements(Z_ARRVAL_P(oid))); + objid_query->vars = (snmpobjarg *)safe_emalloc(sizeof(snmpobjarg), zend_hash_num_elements(Z_ARRVAL_P(oid)), 0); if (objid_query->vars == NULL) { php_error_docref(NULL, E_WARNING, "emalloc() failed while parsing oid array: %s", strerror(errno)); efree(objid_query->vars); diff --git a/ext/standard/base64.c b/ext/standard/base64.c index 64f21044e7..cd6bac0b3d 100644 --- a/ext/standard/base64.c +++ b/ext/standard/base64.c @@ -59,7 +59,7 @@ PHPAPI zend_string *php_base64_encode(const unsigned char *str, size_t length) / unsigned char *p; zend_string *result; - result = zend_string_alloc(((length + 2) / 3) * 4 * sizeof(char), 0); + result = zend_string_safe_alloc(((length + 2) / 3), 4 * sizeof(char), 0, 0); p = (unsigned char *)ZSTR_VAL(result); while (length > 2) { /* keep going until we have less than 24 bits */ diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index 3b129ed5da..a5e00a6df9 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -188,7 +188,8 @@ static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char /* get the response */ result = GET_FTP_RESULT(stream); if (result != 334) { - use_ssl = 0; + php_stream_wrapper_log_error(wrapper, options, "Server doesn't support FTPS."); + goto connect_errexit; } else { /* we must reuse the old SSL session id */ /* if we talk to an old ftpd-ssl */ diff --git a/ext/standard/php_smart_string.h b/ext/standard/php_smart_string.h index 58c319a5a5..7038cff9bb 100644 --- a/ext/standard/php_smart_string.h +++ b/ext/standard/php_smart_string.h @@ -52,19 +52,22 @@ #define SMART_STRING_DO_REALLOC(d, what) \ (d)->c = SMART_STRING_REALLOC((d)->c, (d)->a + 1, (what)) -#define smart_string_alloc4(d, n, what, newlen) do { \ +#define smart_string_alloc4(d, n, what, newlen) do { \ if (!(d)->c) { \ (d)->len = 0; \ newlen = (n); \ - (d)->a = newlen < SMART_STRING_START_SIZE \ - ? SMART_STRING_START_SIZE \ - : newlen + SMART_STRING_PREALLOC; \ - SMART_STRING_DO_REALLOC(d, what); \ + (d)->a = newlen < SMART_STRING_START_SIZE \ + ? SMART_STRING_START_SIZE \ + : newlen + SMART_STRING_PREALLOC; \ + SMART_STRING_DO_REALLOC(d, what); \ } else { \ + if(UNEXPECTED(n > SIZE_MAX - (d)->len)) { \ + zend_error(E_ERROR, "String size overflow"); \ + } \ newlen = (d)->len + (n); \ if (newlen >= (d)->a) { \ - (d)->a = newlen + SMART_STRING_PREALLOC; \ - SMART_STRING_DO_REALLOC(d, what); \ + (d)->a = newlen + SMART_STRING_PREALLOC; \ + SMART_STRING_DO_REALLOC(d, what); \ } \ } \ } while (0) diff --git a/ext/standard/quot_print.c b/ext/standard/quot_print.c index a55b1e5edc..be352cbf80 100644 --- a/ext/standard/quot_print.c +++ b/ext/standard/quot_print.c @@ -275,7 +275,7 @@ PHP_FUNCTION(quoted_printable_encode) RETURN_EMPTY_STRING(); } - new_str = php_quot_print_encode((unsigned char *)ZSTR_VAL(str), (size_t)ZSTR_LEN(str)); + new_str = php_quot_print_encode((unsigned char *)ZSTR_VAL(str), ZSTR_LEN(str)); RETURN_STR(new_str); } /* }}} */ diff --git a/ext/standard/tests/serialize/bug72663.phpt b/ext/standard/tests/serialize/bug72663.phpt new file mode 100644 index 0000000000..c50591ca96 --- /dev/null +++ b/ext/standard/tests/serialize/bug72663.phpt @@ -0,0 +1,56 @@ +--TEST-- +Bug #72663 (1): Don't call __destruct if __wakeup not called or fails +--FILE-- +<?php + +class Test1 { + public function __wakeup() { + echo "Wakeup\n"; + } + public function __destruct() { + echo "Dtor\n"; + } +} + +class Test2 { + public function __wakeup() { + throw new Exception('Unserialization forbidden'); + } + public function __destruct() { + echo "Dtor\n"; + } +} + +// Unserialize object with error in properties +$s = 'O:5:"Test1":1:{s:10:"";}'; +var_dump(unserialize($s)); + +// Variation: Object is turned into a reference +$s = 'O:5:"Test1":2:{i:0;R:1;s:10:"";}'; +var_dump(unserialize($s)); + +// Unserialize object with throwing __wakeup +$s = 'O:5:"Test2":0:{}'; +try { + var_dump(unserialize($s)); +} catch (Exception $e) { + echo "Caught\n"; +} +// +// Variation: Object is turned into a reference +$s = 'O:5:"Test2":1:{i:0;R:1;}'; +try { + var_dump(unserialize($s)); +} catch (Exception $e) { + echo "Caught\n"; +} + +?> +--EXPECTF-- +Notice: unserialize(): Error at offset 17 of 24 bytes in %s on line %d +bool(false) + +Notice: unserialize(): Error at offset 25 of 32 bytes in %s on line %d +bool(false) +Caught +Caught diff --git a/ext/standard/tests/serialize/bug72663_2.phpt b/ext/standard/tests/serialize/bug72663_2.phpt new file mode 100644 index 0000000000..8825dc5efc --- /dev/null +++ b/ext/standard/tests/serialize/bug72663_2.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #72663 (2): Don't allow references into failed unserialize +--FILE-- +<?php + +class obj implements Serializable { + public $data; + function serialize() { + return serialize($this->data); + } + function unserialize($data) { + $this->data = unserialize($data); + } +} + +$inner = 'a:1:{i:0;O:9:"Exception":2:{s:7:"'."\0".'*'."\0".'file";R:4;}'; +$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:4;}'; +var_dump(unserialize($exploit)); + +?> +--EXPECTF-- +Notice: unserialize(): Unexpected end of serialized data in %s on line %d + +Notice: unserialize(): Error at offset 46 of 47 bytes in %s on line %d + +Notice: unserialize(): Error at offset 79 of 80 bytes in %s on line %d +bool(false) diff --git a/ext/standard/tests/serialize/bug72663_3.phpt b/ext/standard/tests/serialize/bug72663_3.phpt new file mode 100644 index 0000000000..37d67706f2 --- /dev/null +++ b/ext/standard/tests/serialize/bug72663_3.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #72663 (3): If unserialization fails, don't initialize the session with the result +--SKIPIF-- +<?php if (!extension_loaded('session')) die('skip Session extension required'); ?> +--INI-- +session.serialize_handler=php_serialize +--FILE-- +<?php +session_start(); +$sess = 'O:9:"Exception":2:{s:7:"'."\0".'*'."\0".'file";R:1;}'; +session_decode($sess); +var_dump($_SESSION); +?> +--EXPECTF-- +Notice: session_decode(): Unexpected end of serialized data in %s on line %d +array(0) { +} diff --git a/ext/standard/tests/streams/bug72853.phpt b/ext/standard/tests/streams/bug72853.phpt new file mode 100644 index 0000000000..48bd60e7a6 --- /dev/null +++ b/ext/standard/tests/streams/bug72853.phpt @@ -0,0 +1,59 @@ +--TEST-- +Bug #72853 (stream_set_blocking doesn't work) +--SKIPIF-- +<?php +if(substr(PHP_OS, 0, 3) == 'WIN' ) { + die('skip not for windows'); +} +?> +--FILE-- +<?php + +$descs = array( + 0 => array('pipe', 'r'), // stdin + 1 => array('pipe', 'w'), // stdout +); + +$p = proc_open("ls", $descs, $pipes, '.', NULL, NULL); + +stream_set_blocking($pipes[1], false); +var_dump(stream_get_meta_data($pipes[1])); +stream_set_blocking($pipes[1], true); +while ($outs = fgets($pipes[1], 1024)) { +} +var_dump(stream_get_meta_data($pipes[1])); +proc_close($p); +?> +--EXPECTF-- +array(7) { + ["timed_out"]=> + bool(false) + ["blocked"]=> + bool(false) + ["eof"]=> + bool(false) + ["stream_type"]=> + string(5) "STDIO" + ["mode"]=> + string(1) "r" + ["unread_bytes"]=> + int(0) + ["seekable"]=> + bool(false) +} +array(7) { + ["timed_out"]=> + bool(false) + ["blocked"]=> + bool(true) + ["eof"]=> + bool(true) + ["stream_type"]=> + string(5) "STDIO" + ["mode"]=> + string(1) "r" + ["unread_bytes"]=> + int(0) + ["seekable"]=> + bool(false) +} diff --git a/ext/standard/tests/strings/bug70436.phpt b/ext/standard/tests/strings/bug70436.phpt new file mode 100644 index 0000000000..c62e468726 --- /dev/null +++ b/ext/standard/tests/strings/bug70436.phpt @@ -0,0 +1,65 @@ +--TEST-- +Bug #70436: Use After Free Vulnerability in unserialize() +--FILE-- +<?php + +class obj implements Serializable +{ + var $data; + + function serialize() + { + return serialize($this->data); + } + + function unserialize($data) + { + $this->data = unserialize($data); + } +} + +$fakezval = ptr2str(1122334455); +$fakezval .= ptr2str(0); +$fakezval .= "\x00\x00\x00\x00"; +$fakezval .= "\x01"; +$fakezval .= "\x00"; +$fakezval .= "\x00\x00"; + +$inner = 'C:3:"obj":3:{ryat'; +$exploit = 'a:4:{i:0;i:1;i:1;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:2;s:'.strlen($fakezval).':"'.$fakezval.'";i:3;R:5;}'; + +$data = unserialize($exploit); + +var_dump($data); + +function ptr2str($ptr) +{ + $out = ''; + + for ($i = 0; $i < 8; $i++) { + $out .= chr($ptr & 0xff); + $ptr >>= 8; + } + + return $out; +} +?> +DONE +--EXPECTF-- +Notice: unserialize(): Error at offset 0 of 3 bytes in %sbug70436.php on line %d + +Notice: unserialize(): Error at offset 17 of 17 bytes in %sbug70436.php on line %d +array(4) { + [0]=> + int(1) + [1]=> + object(obj)#%d (1) { + ["data"]=> + bool(false) + } + [2]=> + string(24) "%s" + [3]=> + bool(false) +} +DONE
\ No newline at end of file diff --git a/ext/standard/tests/strings/bug72663.phpt b/ext/standard/tests/strings/bug72663.phpt new file mode 100644 index 0000000000..e61f939d4d --- /dev/null +++ b/ext/standard/tests/strings/bug72663.phpt @@ -0,0 +1,26 @@ +--TEST-- +Bug #72663: Create an Unexpected Object and Don't Invoke __wakeup() in Deserialization +--FILE-- +<?php +class obj implements Serializable { + var $data; + function serialize() { + return serialize($this->data); + } + function unserialize($data) { + $this->data = unserialize($data); + } +} + +$inner = 'a:1:{i:0;O:9:"Exception":2:{s:7:"'."\0".'*'."\0".'file";R:4;}'; +$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:4;}'; + +$data = unserialize($exploit); +echo $data[1]; +?> +DONE +--EXPECTF-- +Notice: unserialize(): Unexpected end of serialized data in %sbug72663.php on line %d + +Notice: unserialize(): Error at offset 46 of 47 bytes in %sbug72663.php on line %d +DONE
\ No newline at end of file diff --git a/ext/standard/tests/strings/bug72663_2.phpt b/ext/standard/tests/strings/bug72663_2.phpt new file mode 100644 index 0000000000..ac605e9fd2 --- /dev/null +++ b/ext/standard/tests/strings/bug72663_2.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #72663: Create an Unexpected Object and Don't Invoke __wakeup() in Deserialization +--FILE-- +<?php + +ini_set('session.serialize_handler', 'php_serialize'); +session_start(); +$sess = 'O:9:"Exception":2:{s:7:"'."\0".'*'."\0".'file";R:1;}'; +session_decode($sess); +var_dump($_SESSION); +?> +DONE +--EXPECTF-- +Notice: session_decode(): Unexpected end of serialized data in %sbug72663_2.php on line %d +array(0) { +} +DONE
\ No newline at end of file diff --git a/ext/standard/tests/strings/bug72663_3.phpt b/ext/standard/tests/strings/bug72663_3.phpt new file mode 100644 index 0000000000..f7d66efd17 --- /dev/null +++ b/ext/standard/tests/strings/bug72663_3.phpt @@ -0,0 +1,20 @@ +--TEST-- +Bug #72663: Create an Unexpected Object and Don't Invoke __wakeup() in Deserialization +--XFAIL-- +Memory leak, TBF later. +--FILE-- +<?php +class obj { + var $ryat; + function __wakeup() { + $this->ryat = str_repeat('A', 0x112); + } +} + +$poc = 'O:8:"stdClass":1:{i:0;O:3:"obj":1:{s:4:"ryat";R:1;'; +unserialize($poc); +?> +DONE +--EXPECTF-- +Notice: unserialize(): Error at offset 51 of 50 bytes in %sbug72663_3.php on line %d +DONE diff --git a/ext/standard/uuencode.c b/ext/standard/uuencode.c index e443337ec4..787569e430 100644 --- a/ext/standard/uuencode.c +++ b/ext/standard/uuencode.c @@ -71,8 +71,10 @@ PHPAPI zend_string *php_uuencode(char *src, size_t src_len) /* {{{ */ char *p, *s, *e, *ee; zend_string *dest; - /* encoded length is ~ 38% greater than the original */ - dest = zend_string_alloc((size_t)ceil(src_len * 1.38) + 46, 0); + /* encoded length is ~ 38% greater than the original + Use 1.5 for easier calculation. + */ + dest = zend_string_safe_alloc(src_len/2, 3, 46, 0); p = ZSTR_VAL(dest); s = src; e = src + src_len; diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index f96fdb40d6..d59231cf89 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.16 */ +/* Generated by re2c 0.13.5 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -336,6 +336,8 @@ static inline size_t parse_uiv(const unsigned char *p) #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes +static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER); + static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) { while (elements-- > 0) { @@ -344,7 +346,7 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab ZVAL_UNDEF(&key); - if (!php_var_unserialize_ex(&key, p, max, NULL, classes)) { + if (!php_var_unserialize_internal(&key, p, max, NULL, classes)) { zval_dtor(&key); return 0; } @@ -400,7 +402,7 @@ string_key: } } - if (!php_var_unserialize_ex(data, p, max, var_hash, classes)) { + if (!php_var_unserialize_internal(data, p, max, var_hash, classes)) { zval_dtor(&key); return 0; } @@ -490,23 +492,32 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements) zval retval; zval fname; HashTable *ht; + zend_bool has_wakeup; if (Z_TYPE_P(rval) != IS_OBJECT) { return 0; } + has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY + && zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1); + ht = Z_OBJPROP_P(rval); zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED)); if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) { + if (has_wakeup) { + ZVAL_DEREF(rval); + GC_FLAGS(Z_OBJ_P(rval)) |= IS_OBJ_DESTRUCTOR_CALLED; + } return 0; } ZVAL_DEREF(rval); - if (Z_OBJCE_P(rval) != PHP_IC_ENTRY && - zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1)) { + if (has_wakeup) { ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1); BG(serialize_lock)++; - call_user_function_ex(CG(function_table), rval, &fname, &retval, 0, 0, 1, NULL); + if (call_user_function_ex(CG(function_table), rval, &fname, &retval, 0, 0, 1, NULL) == FAILURE || Z_ISUNDEF(retval)) { + GC_FLAGS(Z_OBJ_P(rval)) |= IS_OBJ_DESTRUCTOR_CALLED; + } BG(serialize_lock)--; zval_dtor(&fname); zval_dtor(&retval); @@ -517,7 +528,6 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements) } return finish_nested_data(UNSERIALIZE_PASSTHRU); - } #ifdef PHP_WIN32 # pragma optimize("", on) @@ -529,9 +539,35 @@ PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsign return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU); } - PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) { + var_entries *orig_var_entries = (*var_hash)->last; + zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0; + int result; + + result = php_var_unserialize_internal(UNSERIALIZE_PASSTHRU); + + if (!result) { + /* If the unserialization failed, mark all elements that have been added to var_hash + * as NULL. This will forbid their use by other unserialize() calls in the same + * unserialization context. */ + var_entries *e = orig_var_entries; + zend_long s = orig_used_slots; + while (e) { + for (; s < e->used_slots; s++) { + e->data[s] = NULL; + } + + e = e->next; + s = 0; + } + } + + return result; +} + +static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER) +{ const unsigned char *cursor, *limit, *marker, *start; zval *rval_ref; @@ -549,7 +585,7 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) start = cursor; -#line 553 "ext/standard/var_unserializer.c" +#line 589 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -586,510 +622,111 @@ PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; + if ((YYLIMIT - YYCURSOR) < 7) YYFILL(7); yych = *YYCURSOR; switch (yych) { case 'C': - case 'O': goto yy4; + case 'O': goto yy13; case 'N': goto yy5; - case 'R': goto yy6; - case 'S': goto yy7; - case 'a': goto yy8; - case 'b': goto yy9; - case 'd': goto yy10; - case 'i': goto yy11; + case 'R': goto yy2; + case 'S': goto yy10; + case 'a': goto yy11; + case 'b': goto yy6; + case 'd': goto yy8; + case 'i': goto yy7; case 'o': goto yy12; - case 'r': goto yy13; - case 's': goto yy14; - case '}': goto yy15; - default: goto yy2; + case 'r': goto yy4; + case 's': goto yy9; + case '}': goto yy14; + default: goto yy16; } yy2: - ++YYCURSOR; + yych = *(YYMARKER = ++YYCURSOR); + if (yych == ':') goto yy95; yy3: -#line 919 "ext/standard/var_unserializer.re" +#line 955 "ext/standard/var_unserializer.re" { return 0; } -#line 613 "ext/standard/var_unserializer.c" +#line 651 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); - if (yych == ':') goto yy17; + if (yych == ':') goto yy89; goto yy3; yy5: yych = *++YYCURSOR; - if (yych == ';') goto yy19; + if (yych == ';') goto yy87; goto yy3; yy6: yych = *(YYMARKER = ++YYCURSOR); - if (yych == ':') goto yy21; + if (yych == ':') goto yy83; goto yy3; yy7: yych = *(YYMARKER = ++YYCURSOR); - if (yych == ':') goto yy22; + if (yych == ':') goto yy77; goto yy3; yy8: yych = *(YYMARKER = ++YYCURSOR); - if (yych == ':') goto yy23; + if (yych == ':') goto yy53; goto yy3; yy9: yych = *(YYMARKER = ++YYCURSOR); - if (yych == ':') goto yy24; + if (yych == ':') goto yy46; goto yy3; yy10: yych = *(YYMARKER = ++YYCURSOR); - if (yych == ':') goto yy25; + if (yych == ':') goto yy39; goto yy3; yy11: yych = *(YYMARKER = ++YYCURSOR); - if (yych == ':') goto yy26; + if (yych == ':') goto yy32; goto yy3; yy12: yych = *(YYMARKER = ++YYCURSOR); - if (yych == ':') goto yy27; + if (yych == ':') goto yy25; goto yy3; yy13: yych = *(YYMARKER = ++YYCURSOR); - if (yych == ':') goto yy28; + if (yych == ':') goto yy17; goto yy3; yy14: - yych = *(YYMARKER = ++YYCURSOR); - if (yych == ':') goto yy29; - goto yy3; -yy15: ++YYCURSOR; -#line 913 "ext/standard/var_unserializer.re" +#line 949 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } -#line 666 "ext/standard/var_unserializer.c" +#line 700 "ext/standard/var_unserializer.c" +yy16: + yych = *++YYCURSOR; + goto yy3; yy17: yych = *++YYCURSOR; if (yybm[0+yych] & 128) { - goto yy31; + goto yy20; } - if (yych == '+') goto yy30; + if (yych == '+') goto yy19; yy18: YYCURSOR = YYMARKER; goto yy3; yy19: - ++YYCURSOR; -#line 608 "ext/standard/var_unserializer.re" - { - *p = YYCURSOR; - ZVAL_NULL(rval); - return 1; -} -#line 684 "ext/standard/var_unserializer.c" -yy21: - yych = *++YYCURSOR; - if (yych <= ',') { - if (yych == '+') goto yy33; - goto yy18; - } else { - if (yych <= '-') goto yy33; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy34; - goto yy18; - } -yy22: - yych = *++YYCURSOR; - if (yych == '+') goto yy36; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy37; - goto yy18; -yy23: - yych = *++YYCURSOR; - if (yych == '+') goto yy39; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy40; - goto yy18; -yy24: - yych = *++YYCURSOR; - if (yych <= '/') goto yy18; - if (yych <= '1') goto yy42; - goto yy18; -yy25: - yych = *++YYCURSOR; - if (yych <= '/') { - if (yych <= ',') { - if (yych == '+') goto yy43; - goto yy18; - } else { - if (yych <= '-') goto yy44; - if (yych <= '.') goto yy45; - goto yy18; - } - } else { - if (yych <= 'I') { - if (yych <= '9') goto yy46; - if (yych <= 'H') goto yy18; - goto yy48; - } else { - if (yych == 'N') goto yy49; - goto yy18; - } - } -yy26: - yych = *++YYCURSOR; - if (yych <= ',') { - if (yych == '+') goto yy50; - goto yy18; - } else { - if (yych <= '-') goto yy50; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy51; - goto yy18; - } -yy27: - yych = *++YYCURSOR; - if (yych <= ',') { - if (yych == '+') goto yy53; - goto yy18; - } else { - if (yych <= '-') goto yy53; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy54; - goto yy18; - } -yy28: - yych = *++YYCURSOR; - if (yych <= ',') { - if (yych == '+') goto yy56; - goto yy18; - } else { - if (yych <= '-') goto yy56; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy57; - goto yy18; - } -yy29: - yych = *++YYCURSOR; - if (yych == '+') goto yy59; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy60; - goto yy18; -yy30: yych = *++YYCURSOR; if (yybm[0+yych] & 128) { - goto yy31; + goto yy20; } goto yy18; -yy31: +yy20: ++YYCURSOR; if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); yych = *YYCURSOR; if (yybm[0+yych] & 128) { - goto yy31; - } - if (yych <= '/') goto yy18; - if (yych <= ':') goto yy62; - goto yy18; -yy33: - yych = *++YYCURSOR; - if (yych <= '/') goto yy18; - if (yych >= ':') goto yy18; -yy34: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy34; - if (yych == ';') goto yy63; - goto yy18; -yy36: - yych = *++YYCURSOR; - if (yych <= '/') goto yy18; - if (yych >= ':') goto yy18; -yy37: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy37; - if (yych <= ':') goto yy65; - goto yy18; -yy39: - yych = *++YYCURSOR; - if (yych <= '/') goto yy18; - if (yych >= ':') goto yy18; -yy40: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy40; - if (yych <= ':') goto yy66; - goto yy18; -yy42: - yych = *++YYCURSOR; - if (yych == ';') goto yy67; - goto yy18; -yy43: - yych = *++YYCURSOR; - if (yych == '.') goto yy45; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy46; - goto yy18; -yy44: - yych = *++YYCURSOR; - if (yych <= '/') { - if (yych != '.') goto yy18; - } else { - if (yych <= '9') goto yy46; - if (yych == 'I') goto yy48; - goto yy18; - } -yy45: - yych = *++YYCURSOR; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy69; - goto yy18; -yy46: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4); - yych = *YYCURSOR; - if (yych <= ':') { - if (yych <= '.') { - if (yych <= '-') goto yy18; - goto yy69; - } else { - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy46; - goto yy18; - } - } else { - if (yych <= 'E') { - if (yych <= ';') goto yy71; - if (yych <= 'D') goto yy18; - goto yy73; - } else { - if (yych == 'e') goto yy73; - goto yy18; - } - } -yy48: - yych = *++YYCURSOR; - if (yych == 'N') goto yy74; - goto yy18; -yy49: - yych = *++YYCURSOR; - if (yych == 'A') goto yy75; - goto yy18; -yy50: - yych = *++YYCURSOR; - if (yych <= '/') goto yy18; - if (yych >= ':') goto yy18; -yy51: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy51; - if (yych == ';') goto yy76; - goto yy18; -yy53: - yych = *++YYCURSOR; - if (yych <= '/') goto yy18; - if (yych >= ':') goto yy18; -yy54: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy54; - if (yych <= ':') goto yy78; - goto yy18; -yy56: - yych = *++YYCURSOR; - if (yych <= '/') goto yy18; - if (yych >= ':') goto yy18; -yy57: - ++YYCURSOR; - if (YYLIMIT <= YYCURSOR) YYFILL(1); - yych = *YYCURSOR; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy57; - if (yych == ';') goto yy79; - goto yy18; -yy59: - yych = *++YYCURSOR; - if (yych <= '/') goto yy18; - if (yych >= ':') goto yy18; -yy60: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); - yych = *YYCURSOR; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy60; - if (yych <= ':') goto yy81; - goto yy18; -yy62: - yych = *++YYCURSOR; - if (yych == '"') goto yy82; - goto yy18; -yy63: - ++YYCURSOR; -#line 557 "ext/standard/var_unserializer.re" - { - zend_long id; - - *p = YYCURSOR; - if (!var_hash) return 0; - - id = parse_iv(start + 2) - 1; - if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) { - return 0; - } - - zval_ptr_dtor(rval); - if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) { - ZVAL_UNDEF(rval); - return 1; - } - if (Z_ISREF_P(rval_ref)) { - ZVAL_COPY(rval, rval_ref); - } else { - ZVAL_NEW_REF(rval_ref, rval_ref); - ZVAL_COPY(rval, rval_ref); - } - - return 1; -} -#line 960 "ext/standard/var_unserializer.c" -yy65: - yych = *++YYCURSOR; - if (yych == '"') goto yy84; - goto yy18; -yy66: - yych = *++YYCURSOR; - if (yych == '{') goto yy86; - goto yy18; -yy67: - ++YYCURSOR; -#line 614 "ext/standard/var_unserializer.re" - { - *p = YYCURSOR; - ZVAL_BOOL(rval, parse_iv(start + 2)); - return 1; -} -#line 977 "ext/standard/var_unserializer.c" -yy69: - ++YYCURSOR; - if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4); - yych = *YYCURSOR; - if (yych <= ';') { - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy69; - if (yych <= ':') goto yy18; - } else { - if (yych <= 'E') { - if (yych <= 'D') goto yy18; - goto yy73; - } else { - if (yych == 'e') goto yy73; - goto yy18; - } - } -yy71: - ++YYCURSOR; -#line 662 "ext/standard/var_unserializer.re" - { -#if SIZEOF_ZEND_LONG == 4 -use_double: -#endif - *p = YYCURSOR; - ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL)); - return 1; -} -#line 1006 "ext/standard/var_unserializer.c" -yy73: - yych = *++YYCURSOR; - if (yych <= ',') { - if (yych == '+') goto yy88; - goto yy18; - } else { - if (yych <= '-') goto yy88; - if (yych <= '/') goto yy18; - if (yych <= '9') goto yy89; - goto yy18; - } -yy74: - yych = *++YYCURSOR; - if (yych == 'F') goto yy91; - goto yy18; -yy75: - yych = *++YYCURSOR; - if (yych == 'N') goto yy91; - goto yy18; -yy76: - ++YYCURSOR; -#line 620 "ext/standard/var_unserializer.re" - { -#if SIZEOF_ZEND_LONG == 4 - int digits = YYCURSOR - start - 3; - - if (start[2] == '-' || start[2] == '+') { - digits--; - } - - /* Use double for large zend_long values that were serialized on a 64-bit system */ - if (digits >= MAX_LENGTH_OF_LONG - 1) { - if (digits == MAX_LENGTH_OF_LONG - 1) { - int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1); - - if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) { - goto use_double; - } - } else { - goto use_double; - } - } -#endif - *p = YYCURSOR; - ZVAL_LONG(rval, parse_iv(start + 2)); - return 1; -} -#line 1054 "ext/standard/var_unserializer.c" -yy78: - yych = *++YYCURSOR; - if (yych == '"') goto yy92; - goto yy18; -yy79: - ++YYCURSOR; -#line 583 "ext/standard/var_unserializer.re" - { - zend_long id; - - *p = YYCURSOR; - if (!var_hash) return 0; - - id = parse_iv(start + 2) - 1; - if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) { - return 0; - } - - if (rval_ref == rval) { - return 0; - } - - if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) { - ZVAL_UNDEF(rval); - return 1; + goto yy20; } - - ZVAL_COPY(rval, rval_ref); - - return 1; -} -#line 1086 "ext/standard/var_unserializer.c" -yy81: + if (yych != ':') goto yy18; yych = *++YYCURSOR; - if (yych == '"') goto yy94; - goto yy18; -yy82: + if (yych != '"') goto yy18; ++YYCURSOR; -#line 768 "ext/standard/var_unserializer.re" +#line 804 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; zend_long elements; @@ -1234,10 +871,105 @@ yy82: return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 1238 "ext/standard/var_unserializer.c" -yy84: +#line 875 "ext/standard/var_unserializer.c" +yy25: + yych = *++YYCURSOR; + if (yych <= ',') { + if (yych != '+') goto yy18; + } else { + if (yych <= '-') goto yy26; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy27; + goto yy18; + } +yy26: + yych = *++YYCURSOR; + if (yych <= '/') goto yy18; + if (yych >= ':') goto yy18; +yy27: + ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); + yych = *YYCURSOR; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy27; + if (yych >= ';') goto yy18; + yych = *++YYCURSOR; + if (yych != '"') goto yy18; + ++YYCURSOR; +#line 797 "ext/standard/var_unserializer.re" + { + if (!var_hash) return 0; + + return object_common2(UNSERIALIZE_PASSTHRU, + object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); +} +#line 907 "ext/standard/var_unserializer.c" +yy32: + yych = *++YYCURSOR; + if (yych == '+') goto yy33; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy34; + goto yy18; +yy33: + yych = *++YYCURSOR; + if (yych <= '/') goto yy18; + if (yych >= ':') goto yy18; +yy34: + ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); + yych = *YYCURSOR; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy34; + if (yych >= ';') goto yy18; + yych = *++YYCURSOR; + if (yych != '{') goto yy18; + ++YYCURSOR; +#line 773 "ext/standard/var_unserializer.re" + { + zend_long elements = parse_iv(start + 2); + /* use iv() not uiv() in order to check data range */ + *p = YYCURSOR; + if (!var_hash) return 0; + + if (elements < 0) { + return 0; + } + + array_init_size(rval, elements); + if (elements) { + /* we can't convert from packed to hash during unserialization, because + reference to some zvals might be keept in var_hash (to support references) */ + zend_hash_real_init(Z_ARRVAL_P(rval), 0); + } + + if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) { + return 0; + } + + return finish_nested_data(UNSERIALIZE_PASSTHRU); +} +#line 952 "ext/standard/var_unserializer.c" +yy39: + yych = *++YYCURSOR; + if (yych == '+') goto yy40; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy41; + goto yy18; +yy40: + yych = *++YYCURSOR; + if (yych <= '/') goto yy18; + if (yych >= ':') goto yy18; +yy41: ++YYCURSOR; -#line 703 "ext/standard/var_unserializer.re" + if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); + yych = *YYCURSOR; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy41; + if (yych >= ';') goto yy18; + yych = *++YYCURSOR; + if (yych != '"') goto yy18; + ++YYCURSOR; +#line 739 "ext/standard/var_unserializer.re" { size_t len, maxlen; zend_string *str; @@ -1271,127 +1003,407 @@ yy84: ZVAL_STR(rval, str); return 1; } -#line 1275 "ext/standard/var_unserializer.c" -yy86: +#line 1007 "ext/standard/var_unserializer.c" +yy46: + yych = *++YYCURSOR; + if (yych == '+') goto yy47; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy48; + goto yy18; +yy47: + yych = *++YYCURSOR; + if (yych <= '/') goto yy18; + if (yych >= ':') goto yy18; +yy48: ++YYCURSOR; -#line 737 "ext/standard/var_unserializer.re" + if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); + yych = *YYCURSOR; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy48; + if (yych >= ';') goto yy18; + yych = *++YYCURSOR; + if (yych != '"') goto yy18; + ++YYCURSOR; +#line 707 "ext/standard/var_unserializer.re" { - zend_long elements = parse_iv(start + 2); - /* use iv() not uiv() in order to check data range */ - *p = YYCURSOR; - if (!var_hash) return 0; + size_t len, maxlen; + char *str; - if (elements < 0) { + len = parse_uiv(start + 2); + maxlen = max - YYCURSOR; + if (maxlen < len) { + *p = start + 2; return 0; } - array_init_size(rval, elements); - if (elements) { - /* we can't convert from packed to hash during unserialization, because - reference to some zvals might be keept in var_hash (to support references) */ - zend_hash_real_init(Z_ARRVAL_P(rval), 0); + str = (char*)YYCURSOR; + + YYCURSOR += len; + + if (*(YYCURSOR) != '"') { + *p = YYCURSOR; + return 0; } - if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) { + if (*(YYCURSOR + 1) != ';') { + *p = YYCURSOR + 1; return 0; } - return finish_nested_data(UNSERIALIZE_PASSTHRU); + YYCURSOR += 2; + *p = YYCURSOR; + + ZVAL_STRINGL(rval, str, len); + return 1; +} +#line 1060 "ext/standard/var_unserializer.c" +yy53: + yych = *++YYCURSOR; + if (yych <= '/') { + if (yych <= ',') { + if (yych == '+') goto yy57; + goto yy18; + } else { + if (yych <= '-') goto yy55; + if (yych <= '.') goto yy60; + goto yy18; + } + } else { + if (yych <= 'I') { + if (yych <= '9') goto yy58; + if (yych <= 'H') goto yy18; + goto yy56; + } else { + if (yych != 'N') goto yy18; + } + } + yych = *++YYCURSOR; + if (yych == 'A') goto yy76; + goto yy18; +yy55: + yych = *++YYCURSOR; + if (yych <= '/') { + if (yych == '.') goto yy60; + goto yy18; + } else { + if (yych <= '9') goto yy58; + if (yych != 'I') goto yy18; + } +yy56: + yych = *++YYCURSOR; + if (yych == 'N') goto yy72; + goto yy18; +yy57: + yych = *++YYCURSOR; + if (yych == '.') goto yy60; + if (yych <= '/') goto yy18; + if (yych >= ':') goto yy18; +yy58: + ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4); + yych = *YYCURSOR; + if (yych <= ':') { + if (yych <= '.') { + if (yych <= '-') goto yy18; + goto yy70; + } else { + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy58; + goto yy18; + } + } else { + if (yych <= 'E') { + if (yych <= ';') goto yy63; + if (yych <= 'D') goto yy18; + goto yy65; + } else { + if (yych == 'e') goto yy65; + goto yy18; + } + } +yy60: + yych = *++YYCURSOR; + if (yych <= '/') goto yy18; + if (yych >= ':') goto yy18; +yy61: + ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4); + yych = *YYCURSOR; + if (yych <= ';') { + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy61; + if (yych <= ':') goto yy18; + } else { + if (yych <= 'E') { + if (yych <= 'D') goto yy18; + goto yy65; + } else { + if (yych == 'e') goto yy65; + goto yy18; + } + } +yy63: + ++YYCURSOR; +#line 698 "ext/standard/var_unserializer.re" + { +#if SIZEOF_ZEND_LONG == 4 +use_double: +#endif + *p = YYCURSOR; + ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL)); + return 1; } -#line 1302 "ext/standard/var_unserializer.c" -yy88: +#line 1157 "ext/standard/var_unserializer.c" +yy65: + yych = *++YYCURSOR; + if (yych <= ',') { + if (yych != '+') goto yy18; + } else { + if (yych <= '-') goto yy66; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy67; + goto yy18; + } +yy66: yych = *++YYCURSOR; if (yych <= ',') { - if (yych == '+') goto yy96; + if (yych == '+') goto yy69; goto yy18; } else { - if (yych <= '-') goto yy96; + if (yych <= '-') goto yy69; if (yych <= '/') goto yy18; if (yych >= ':') goto yy18; } -yy89: +yy67: ++YYCURSOR; if (YYLIMIT <= YYCURSOR) YYFILL(1); yych = *YYCURSOR; if (yych <= '/') goto yy18; - if (yych <= '9') goto yy89; - if (yych == ';') goto yy71; + if (yych <= '9') goto yy67; + if (yych == ';') goto yy63; goto yy18; -yy91: +yy69: yych = *++YYCURSOR; - if (yych == ';') goto yy97; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy67; goto yy18; -yy92: +yy70: + ++YYCURSOR; + if ((YYLIMIT - YYCURSOR) < 4) YYFILL(4); + yych = *YYCURSOR; + if (yych <= ';') { + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy70; + if (yych <= ':') goto yy18; + goto yy63; + } else { + if (yych <= 'E') { + if (yych <= 'D') goto yy18; + goto yy65; + } else { + if (yych == 'e') goto yy65; + goto yy18; + } + } +yy72: + yych = *++YYCURSOR; + if (yych != 'F') goto yy18; +yy73: + yych = *++YYCURSOR; + if (yych != ';') goto yy18; ++YYCURSOR; -#line 761 "ext/standard/var_unserializer.re" +#line 682 "ext/standard/var_unserializer.re" { - if (!var_hash) return 0; + *p = YYCURSOR; - return object_common2(UNSERIALIZE_PASSTHRU, - object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); + if (!strncmp((char*)start + 2, "NAN", 3)) { + ZVAL_DOUBLE(rval, php_get_nan()); + } else if (!strncmp((char*)start + 2, "INF", 3)) { + ZVAL_DOUBLE(rval, php_get_inf()); + } else if (!strncmp((char*)start + 2, "-INF", 4)) { + ZVAL_DOUBLE(rval, -php_get_inf()); + } else { + ZVAL_NULL(rval); + } + + return 1; } -#line 1334 "ext/standard/var_unserializer.c" -yy94: +#line 1232 "ext/standard/var_unserializer.c" +yy76: + yych = *++YYCURSOR; + if (yych == 'N') goto yy73; + goto yy18; +yy77: + yych = *++YYCURSOR; + if (yych <= ',') { + if (yych != '+') goto yy18; + } else { + if (yych <= '-') goto yy78; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy79; + goto yy18; + } +yy78: + yych = *++YYCURSOR; + if (yych <= '/') goto yy18; + if (yych >= ':') goto yy18; +yy79: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy79; + if (yych != ';') goto yy18; ++YYCURSOR; -#line 671 "ext/standard/var_unserializer.re" +#line 656 "ext/standard/var_unserializer.re" { - size_t len, maxlen; - char *str; +#if SIZEOF_ZEND_LONG == 4 + int digits = YYCURSOR - start - 3; - len = parse_uiv(start + 2); - maxlen = max - YYCURSOR; - if (maxlen < len) { - *p = start + 2; - return 0; + if (start[2] == '-' || start[2] == '+') { + digits--; } - str = (char*)YYCURSOR; + /* Use double for large zend_long values that were serialized on a 64-bit system */ + if (digits >= MAX_LENGTH_OF_LONG - 1) { + if (digits == MAX_LENGTH_OF_LONG - 1) { + int cmp = strncmp((char*)YYCURSOR - MAX_LENGTH_OF_LONG, long_min_digits, MAX_LENGTH_OF_LONG - 1); - YYCURSOR += len; + if (!(cmp < 0 || (cmp == 0 && start[2] == '-'))) { + goto use_double; + } + } else { + goto use_double; + } + } +#endif + *p = YYCURSOR; + ZVAL_LONG(rval, parse_iv(start + 2)); + return 1; +} +#line 1285 "ext/standard/var_unserializer.c" +yy83: + yych = *++YYCURSOR; + if (yych <= '/') goto yy18; + if (yych >= '2') goto yy18; + yych = *++YYCURSOR; + if (yych != ';') goto yy18; + ++YYCURSOR; +#line 650 "ext/standard/var_unserializer.re" + { + *p = YYCURSOR; + ZVAL_BOOL(rval, parse_iv(start + 2)); + return 1; +} +#line 1299 "ext/standard/var_unserializer.c" +yy87: + ++YYCURSOR; +#line 644 "ext/standard/var_unserializer.re" + { + *p = YYCURSOR; + ZVAL_NULL(rval); + return 1; +} +#line 1308 "ext/standard/var_unserializer.c" +yy89: + yych = *++YYCURSOR; + if (yych <= ',') { + if (yych != '+') goto yy18; + } else { + if (yych <= '-') goto yy90; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy91; + goto yy18; + } +yy90: + yych = *++YYCURSOR; + if (yych <= '/') goto yy18; + if (yych >= ':') goto yy18; +yy91: + ++YYCURSOR; + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy91; + if (yych != ';') goto yy18; + ++YYCURSOR; +#line 619 "ext/standard/var_unserializer.re" + { + zend_long id; - if (*(YYCURSOR) != '"') { - *p = YYCURSOR; + *p = YYCURSOR; + if (!var_hash) return 0; + + id = parse_iv(start + 2) - 1; + if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) { return 0; } - if (*(YYCURSOR + 1) != ';') { - *p = YYCURSOR + 1; + if (rval_ref == rval) { return 0; } - YYCURSOR += 2; - *p = YYCURSOR; + if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) { + ZVAL_UNDEF(rval); + return 1; + } + + ZVAL_COPY(rval, rval_ref); - ZVAL_STRINGL(rval, str, len); return 1; } -#line 1369 "ext/standard/var_unserializer.c" +#line 1356 "ext/standard/var_unserializer.c" +yy95: + yych = *++YYCURSOR; + if (yych <= ',') { + if (yych != '+') goto yy18; + } else { + if (yych <= '-') goto yy96; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy97; + goto yy18; + } yy96: yych = *++YYCURSOR; if (yych <= '/') goto yy18; - if (yych <= '9') goto yy89; - goto yy18; + if (yych >= ':') goto yy18; yy97: ++YYCURSOR; -#line 646 "ext/standard/var_unserializer.re" + if (YYLIMIT <= YYCURSOR) YYFILL(1); + yych = *YYCURSOR; + if (yych <= '/') goto yy18; + if (yych <= '9') goto yy97; + if (yych != ';') goto yy18; + ++YYCURSOR; +#line 593 "ext/standard/var_unserializer.re" { - *p = YYCURSOR; + zend_long id; - if (!strncmp((char*)start + 2, "NAN", 3)) { - ZVAL_DOUBLE(rval, php_get_nan()); - } else if (!strncmp((char*)start + 2, "INF", 3)) { - ZVAL_DOUBLE(rval, php_get_inf()); - } else if (!strncmp((char*)start + 2, "-INF", 4)) { - ZVAL_DOUBLE(rval, -php_get_inf()); + *p = YYCURSOR; + if (!var_hash) return 0; + + id = parse_iv(start + 2) - 1; + if (id == -1 || (rval_ref = var_access(var_hash, id)) == NULL) { + return 0; + } + + zval_ptr_dtor(rval); + if (Z_ISUNDEF_P(rval_ref) || (Z_ISREF_P(rval_ref) && Z_ISUNDEF_P(Z_REFVAL_P(rval_ref)))) { + ZVAL_UNDEF(rval); + return 1; + } + if (Z_ISREF_P(rval_ref)) { + ZVAL_COPY(rval, rval_ref); } else { - ZVAL_NULL(rval); + ZVAL_NEW_REF(rval_ref, rval_ref); + ZVAL_COPY(rval, rval_ref); } return 1; } -#line 1393 "ext/standard/var_unserializer.c" +#line 1405 "ext/standard/var_unserializer.c" } -#line 921 "ext/standard/var_unserializer.re" +#line 957 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 4ba8f75f3b..90762a41d0 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -340,6 +340,8 @@ static inline size_t parse_uiv(const unsigned char *p) #define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes #define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes +static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER); + static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops) { while (elements-- > 0) { @@ -348,7 +350,7 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab ZVAL_UNDEF(&key); - if (!php_var_unserialize_ex(&key, p, max, NULL, classes)) { + if (!php_var_unserialize_internal(&key, p, max, NULL, classes)) { zval_dtor(&key); return 0; } @@ -404,7 +406,7 @@ string_key: } } - if (!php_var_unserialize_ex(data, p, max, var_hash, classes)) { + if (!php_var_unserialize_internal(data, p, max, var_hash, classes)) { zval_dtor(&key); return 0; } @@ -494,23 +496,32 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements) zval retval; zval fname; HashTable *ht; + zend_bool has_wakeup; if (Z_TYPE_P(rval) != IS_OBJECT) { return 0; } + has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY + && zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1); + ht = Z_OBJPROP_P(rval); zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, (ht->u.flags & HASH_FLAG_PACKED)); if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) { + if (has_wakeup) { + ZVAL_DEREF(rval); + GC_FLAGS(Z_OBJ_P(rval)) |= IS_OBJ_DESTRUCTOR_CALLED; + } return 0; } ZVAL_DEREF(rval); - if (Z_OBJCE_P(rval) != PHP_IC_ENTRY && - zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1)) { + if (has_wakeup) { ZVAL_STRINGL(&fname, "__wakeup", sizeof("__wakeup") - 1); BG(serialize_lock)++; - call_user_function_ex(CG(function_table), rval, &fname, &retval, 0, 0, 1, NULL); + if (call_user_function_ex(CG(function_table), rval, &fname, &retval, 0, 0, 1, NULL) == FAILURE || Z_ISUNDEF(retval)) { + GC_FLAGS(Z_OBJ_P(rval)) |= IS_OBJ_DESTRUCTOR_CALLED; + } BG(serialize_lock)--; zval_dtor(&fname); zval_dtor(&retval); @@ -521,7 +532,6 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements) } return finish_nested_data(UNSERIALIZE_PASSTHRU); - } #ifdef PHP_WIN32 # pragma optimize("", on) @@ -533,9 +543,35 @@ PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsign return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU); } - PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) { + var_entries *orig_var_entries = (*var_hash)->last; + zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0; + int result; + + result = php_var_unserialize_internal(UNSERIALIZE_PASSTHRU); + + if (!result) { + /* If the unserialization failed, mark all elements that have been added to var_hash + * as NULL. This will forbid their use by other unserialize() calls in the same + * unserialization context. */ + var_entries *e = orig_var_entries; + zend_long s = orig_used_slots; + while (e) { + for (; s < e->used_slots; s++) { + e->data[s] = NULL; + } + + e = e->next; + s = 0; + } + } + + return result; +} + +static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER) +{ const unsigned char *cursor, *limit, *marker, *start; zval *rval_ref; diff --git a/ext/wddx/tests/bug72142.phpt b/ext/wddx/tests/bug72142.phpt index 3976bb2554..0c93ecc970 100644 --- a/ext/wddx/tests/bug72142.phpt +++ b/ext/wddx/tests/bug72142.phpt @@ -1,5 +1,7 @@ --TEST-- Bug #72142: WDDX Packet Injection Vulnerability in wddx_serialize_value() +--SKIPIF-- +<?php if (!extension_loaded("wddx")) print "skip"; ?> --FILE-- <?php diff --git a/ext/wddx/tests/bug72749.phpt b/ext/wddx/tests/bug72749.phpt new file mode 100644 index 0000000000..ee17d0f229 --- /dev/null +++ b/ext/wddx/tests/bug72749.phpt @@ -0,0 +1,34 @@ +--TEST-- +Bug #72749: wddx_deserialize allows illegal memory access +--SKIPIF-- +<?php +if (!extension_loaded('wddx')) { + die('skip. wddx not available'); +} +?> +--FILE-- +<?php +$xml = <<<XML +<?xml version='1.0'?> +<!DOCTYPE wddxPacket SYSTEM 'wddx_0100.dtd'> +<wddxPacket version='1.0'> +<header/> + <data> + <struct> + <var name='aDateTime3'> + <dateTime>2\r2004-09-10T05:52:49+00</dateTime> + </var> + </struct> + </data> +</wddxPacket> +XML; + +$array = wddx_deserialize($xml); +var_dump($array); +?> +--EXPECT-- +array(1) { + ["aDateTime3"]=> + string(24) "2 +2004-09-10T05:52:49+00" +} diff --git a/ext/wddx/tests/bug72750.phpt b/ext/wddx/tests/bug72750.phpt new file mode 100644 index 0000000000..3a6794df28 --- /dev/null +++ b/ext/wddx/tests/bug72750.phpt @@ -0,0 +1,34 @@ +--TEST-- +Bug #72750: wddx_deserialize null dereference +--SKIPIF-- +<?php +if (!extension_loaded('wddx')) { + die('skip. wddx not available'); +} +?> +--FILE-- +<?php + +$xml = <<< XML +<?xml version='1.0'?> +<!DOCTYPE wddxPacket SYSTEM 'wddx_0100.dtd'> +<wddxPacket version='1.0'> +<header/> + <data> + <struct> + <var name='aBinary'> + <binary length='11'>\\tYmluYXJRhdGE=</binary> + </var> + </struct> + </data> +</wddxPacket> +XML; + +$array = wddx_deserialize($xml); +var_dump($array); +?> +--EXPECT-- +array(1) { + ["aBinary"]=> + string(0) "" +} diff --git a/ext/wddx/tests/bug72790.phpt b/ext/wddx/tests/bug72790.phpt new file mode 100644 index 0000000000..5bc4478401 --- /dev/null +++ b/ext/wddx/tests/bug72790.phpt @@ -0,0 +1,36 @@ +--TEST-- +Bug 72790: wddx_deserialize null dereference with invalid xml +--SKIPIF-- +<?php +if (!extension_loaded('wddx')) { + die('skip. wddx not available'); +} +?> +--FILE-- +<?php + +$xml = <<< XML +<?xml version='1.0' ?> +<!DOCTYPE wddxPacket SYSTEM 'wddx_0100.dtd'> +<wddxPacket version='1.0'> + |array> + <var name="XXXX"> + <boolean value="this"> + </boolean> + </var> + <var name="YYYY"> + <var name="UUUU"> + <var name="EZEZ"> + </var> + </var> + </var> + </array> +</wddxPacket> +XML; + +$array = wddx_deserialize($xml); +var_dump($array); +?> +--EXPECTF-- +Notice: Undefined variable: array in %s%ebug72790.php on line %d +NULL diff --git a/ext/wddx/tests/bug72799.phpt b/ext/wddx/tests/bug72799.phpt new file mode 100644 index 0000000000..53ac95bd6a --- /dev/null +++ b/ext/wddx/tests/bug72799.phpt @@ -0,0 +1,29 @@ +--TEST-- +Bug #72799: wddx_deserialize null dereference in php_wddx_pop_element +--SKIPIF-- +<?php +if (!extension_loaded('wddx')) { + die('skip. wddx not available'); +} +?> +--FILE-- +<?php + +$xml = <<<XML +<?xml version='1.0'?> +<!DOCTYPE wddxPacket SYSTEM 'wddx_0100.dtd'> +<wddxPacket version="1.0"> + <var name="XXXX"> + <boolean value="1"> + <dateTime>1998-06-12T04:32:12+00</dateTime> + </boolean> + </var> +</wddxPacket> +XML; + +$array = wddx_deserialize($xml); +var_dump($array); +?> +--EXPECTF-- +Notice: Undefined variable: array in %s%ebug72799.php on line 16 +NULL diff --git a/ext/wddx/wddx.c b/ext/wddx/wddx.c index d2171b47c3..2d68406be9 100644 --- a/ext/wddx/wddx.c +++ b/ext/wddx/wddx.c @@ -886,10 +886,10 @@ static void php_wddx_pop_element(void *user_data, const XML_Char *name) if (Z_TYPE(ent1->data) == IS_UNDEF) { if (stack->top > 1) { stack->top--; + efree(ent1); } else { stack->done = 1; } - efree(ent1); return; } @@ -897,7 +897,11 @@ static void php_wddx_pop_element(void *user_data, const XML_Char *name) zend_string *new_str = php_base64_decode( (unsigned char *)Z_STRVAL(ent1->data), Z_STRLEN(ent1->data)); zval_ptr_dtor(&ent1->data); - ZVAL_STR(&ent1->data, new_str); + if (new_str) { + ZVAL_STR(&ent1->data, new_str); + } else { + ZVAL_EMPTY_STRING(&ent1->data); + } } /* Call __wakeup() method on the object. */ @@ -917,7 +921,7 @@ static void php_wddx_pop_element(void *user_data, const XML_Char *name) wddx_stack_top(stack, (void**)&ent2); /* if non-existent field */ - if (ent2->type == ST_FIELD && Z_ISUNDEF(ent2->data)) { + if (Z_ISUNDEF(ent2->data)) { zval_ptr_dtor(&ent1->data); efree(ent1); return; @@ -1030,16 +1034,23 @@ static void php_wddx_process_data(void *user_data, const XML_Char *s, int len) case ST_DATETIME: { char *tmp; - tmp = emalloc(len + 1); - memcpy(tmp, (char *)s, len); + if (Z_TYPE(ent->data) == IS_STRING) { + tmp = safe_emalloc(Z_STRLEN(ent->data), 1, (size_t)len + 1); + memcpy(tmp, Z_STRVAL(ent->data), Z_STRLEN(ent->data)); + memcpy(tmp + Z_STRLEN(ent->data), s, len); + len += Z_STRLEN(ent->data); + zval_dtor(&ent->data); + } else { + tmp = emalloc(len + 1); + memcpy(tmp, (char *)s, len); + } tmp[len] = '\0'; - Z_LVAL(ent->data) = php_parse_date(tmp, NULL); + ZVAL_LONG(&ent->data, php_parse_date(tmp, NULL)); /* date out of range < 1969 or > 2038 */ if (Z_LVAL(ent->data) == -1) { - ZVAL_STRINGL(&ent->data, (char *)s, len); + ZVAL_STRINGL(&ent->data, (char *)tmp, len); } - efree(tmp); } break; @@ -1073,8 +1084,12 @@ int php_wddx_deserialize_ex(const char *value, size_t vallen, zval *return_value if (stack.top == 1) { wddx_stack_top(&stack, (void**)&ent); - ZVAL_COPY(return_value, &ent->data); - retval = SUCCESS; + if (Z_ISUNDEF(ent->data)) { + retval = FAILURE; + } else { + ZVAL_COPY(return_value, &ent->data); + retval = SUCCESS; + } } else { retval = FAILURE; } diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c index c3646ee0fd..bf78db3bdf 100644 --- a/main/fopen_wrappers.c +++ b/main/fopen_wrappers.c @@ -144,7 +144,7 @@ PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path char *path_file; int resolved_basedir_len; int resolved_name_len; - int path_len; + size_t path_len; int nesting_level = 0; /* Special case basedir==".": Use script-directory */ @@ -153,7 +153,7 @@ PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path strlcpy(local_open_basedir, basedir, sizeof(local_open_basedir)); } - path_len = (int)strlen(path); + path_len = strlen(path); if (path_len > (MAXPATHLEN - 1)) { /* empty and too long paths are invalid */ return -1; @@ -164,7 +164,7 @@ PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path return -1; } - path_len = (int)strlen(resolved_name); + path_len = strlen(resolved_name); memcpy(path_tmp, resolved_name, path_len + 1); /* safe */ while (VCWD_REALPATH(path_tmp, resolved_name) == NULL) { diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 7ede3718b5..69dd25acc5 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -857,7 +857,19 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void data->is_pipe_blocking = value; return PHP_STREAM_OPTION_RETURN_OK; #endif + case PHP_STREAM_OPTION_META_DATA_API: + if (fd == -1) + return -1; +#ifdef O_NONBLOCK + flags = fcntl(fd, F_GETFL, 0); + add_assoc_bool((zval*)ptrparam, "timed_out", 0); + add_assoc_bool((zval*)ptrparam, "blocked", (flags & O_NONBLOCK)? 0 : 1); + add_assoc_bool((zval*)ptrparam, "eof", stream->eof); + + return PHP_STREAM_OPTION_RETURN_OK; +#endif + return -1; default: return PHP_STREAM_OPTION_RETURN_NOTIMPL; } |