From b221df5549533fe04c151cca85be43eb624a643d Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 4 Aug 2015 23:56:15 -0700 Subject: 5.4.45 next --- NEWS | 4 +++- configure.in | 2 +- main/php_version.h | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 1dcbbd2c56..a3b5d82910 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? 2015 PHP 5.4.44 +?? ??? 2015 PHP 5.4.45 + +06 Aug 2015 PHP 5.4.44 - Core: . Fixed bug #69793 (Remotely triggerable stack exhaustion via recursive diff --git a/configure.in b/configure.in index 2c2edc9426..2c12f5471d 100644 --- a/configure.in +++ b/configure.in @@ -119,7 +119,7 @@ int zend_sprintf(char *buffer, const char *format, ...); PHP_MAJOR_VERSION=5 PHP_MINOR_VERSION=4 -PHP_RELEASE_VERSION=44 +PHP_RELEASE_VERSION=45 PHP_EXTRA_VERSION="-dev" PHP_VERSION="$PHP_MAJOR_VERSION.$PHP_MINOR_VERSION.$PHP_RELEASE_VERSION$PHP_EXTRA_VERSION" PHP_VERSION_ID=`expr [$]PHP_MAJOR_VERSION \* 10000 + [$]PHP_MINOR_VERSION \* 100 + [$]PHP_RELEASE_VERSION` diff --git a/main/php_version.h b/main/php_version.h index 2ff0add16b..49c00afc70 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.in to change version number */ #define PHP_MAJOR_VERSION 5 #define PHP_MINOR_VERSION 4 -#define PHP_RELEASE_VERSION 44 +#define PHP_RELEASE_VERSION 45 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "5.4.44-dev" -#define PHP_VERSION_ID 50444 +#define PHP_VERSION "5.4.45-dev" +#define PHP_VERSION_ID 50445 -- cgit v1.2.1 From dc72378ff0b347be16794cb774db54ec407d0140 Mon Sep 17 00:00:00 2001 From: Lior Kaplan Date: Mon, 10 Aug 2015 11:18:33 +0300 Subject: Add CVE IDs asigned to #69085 (PHP 5.4.39) --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index a3b5d82910..9dd134d2de 100644 --- a/NEWS +++ b/NEWS @@ -163,7 +163,7 @@ PHP NEWS - SOAP: . Fixed bug #69085 (SoapClient's __call() type confusion through - unserialize()). (Dmitry) + unserialize()). (CVE-2015-4147, CVE-2015-4148) (Dmitry) - ZIP: . Fixed bug #69253 (ZIP Integer Overflow leads to writing past heap -- cgit v1.2.1 From 9ff333439721dd42faa2e1c838c178633703ef0c Mon Sep 17 00:00:00 2001 From: Lior Kaplan Date: Mon, 10 Aug 2015 11:19:18 +0300 Subject: Add CVE IDs asigned (post release) to PHP 5.4.43 --- NEWS | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 9dd134d2de..33a818f69b 100644 --- a/NEWS +++ b/NEWS @@ -43,13 +43,14 @@ PHP NEWS from fix to bug #68776. (Yasuo) - Mysqlnd: - . Fixed bug #69669 (mysqlnd is vulnerable to BACKRONYM) (CVE-2015-3152). + . Fixed bug #69669 (mysqlnd is vulnerable to BACKRONYM). (CVE-2015-3152) (Andrey) - Phar: - . Fixed bug #69958 (Segfault in Phar::convertToData on invalid file). (Stas) + . Fixed bug #69958 (Segfault in Phar::convertToData on invalid file). + (CVE-2015-5589) (Stas) . Fixed bug #69923 (Buffer overflow and stack smashing error in - phar_fix_filepath). (Stas) + phar_fix_filepath). (CVE-2015-5590) (Stas) 11 Jun 2015 PHP 5.4.42 -- cgit v1.2.1 From 1744be2d17befc69bf00033993f4081852a747d6 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Sun, 16 Aug 2015 17:16:15 -0700 Subject: Fix for bug #69782 --- ext/xsl/xsltprocessor.c | 142 +++++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 69 deletions(-) diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c index 67c90f501f..d21a8ebcb7 100644 --- a/ext/xsl/xsltprocessor.c +++ b/ext/xsl/xsltprocessor.c @@ -81,10 +81,10 @@ ZEND_END_ARG_INFO(); /* }}} */ /* -* class xsl_xsltprocessor +* class xsl_xsltprocessor * * URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html# -* Since: +* Since: */ const zend_function_entry php_xsl_xsltprocessor_class_functions[] = { @@ -111,9 +111,9 @@ static char *php_xsl_xslt_string_to_xpathexpr(const char *str TSRMLS_DC) xmlChar *value; int str_len; - + str_len = xmlStrlen(string) + 3; - + if (xmlStrchr(string, '"')) { if (xmlStrchr(string, '\'')) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create XPath expression (string contains both quote and double-quotes)"); @@ -133,7 +133,7 @@ static char *php_xsl_xslt_string_to_xpathexpr(const char *str TSRMLS_DC) Translates a PHP array to a libxslt parameters array */ static char **php_xsl_xslt_make_params(HashTable *parht, int xpath_params TSRMLS_DC) { - + int parsize; zval **value; char *xpath_expr, *string_key = NULL; @@ -158,7 +158,7 @@ static char **php_xsl_xslt_make_params(HashTable *parht, int xpath_params TSRMLS SEPARATE_ZVAL(value); convert_to_string(*value); } - + if (!xpath_params) { xpath_expr = php_xsl_xslt_string_to_xpathexpr(Z_STRVAL_PP(value) TSRMLS_CC); } else { @@ -192,7 +192,7 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t char *str; char *callable = NULL; xsl_object *intern; - + TSRMLS_FETCH(); if (! zend_is_executing(TSRMLS_C)) { @@ -219,15 +219,17 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t } } } - + if (error == 1) { for (i = nargs - 1; i >= 0; i--) { obj = valuePop(ctxt); - xmlXPathFreeObject(obj); + if (obj) { + xmlXPathFreeObject(obj); + } } return; } - + fci.param_count = nargs - 1; if (fci.param_count > 0) { fci.params = safe_emalloc(fci.param_count, sizeof(zval**), 0); @@ -265,7 +267,7 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t if (node->type == XML_NAMESPACE_DECL) { xmlNsPtr curns; xmlNodePtr nsparent; - + nsparent = node->_private; curns = xmlNewNs(NULL, node->name, NULL); if (node->children) { @@ -297,14 +299,16 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t xmlXPathFreeObject(obj); fci.params[i] = &args[i]; } - + fci.size = sizeof(fci); fci.function_table = EG(function_table); - + obj = valuePop(ctxt); - if (obj->stringval == NULL) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Handler name must be a string"); - xmlXPathFreeObject(obj); + if (obj == NULL || obj->stringval == NULL) { + if (obj) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Handler name must be a string"); + xmlXPathFreeObject(obj); + } valuePush(ctxt, xmlXPathNewString("")); if (fci.param_count > 0) { for (i = 0; i < nargs - 1; i++) { @@ -313,12 +317,12 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t efree(args); efree(fci.params); } - return; + return; } INIT_PZVAL(&handler); ZVAL_STRING(&handler, obj->stringval, 1); xmlXPathFreeObject(obj); - + fci.function_name = &handler; fci.symbol_table = NULL; fci.object_ptr = NULL; @@ -328,7 +332,7 @@ static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int t if (!zend_make_callable(&handler, &callable TSRMLS_CC)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call handler %s()", callable); valuePush(ctxt, xmlXPathNewString("")); - } else if ( intern->registerPhpFunctions == 2 && zend_hash_exists(intern->registered_phpfunctions, callable, strlen(callable) + 1) == 0) { + } else if ( intern->registerPhpFunctions == 2 && zend_hash_exists(intern->registered_phpfunctions, callable, strlen(callable) + 1) == 0) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not allowed to call handler '%s()'", callable); /* Push an empty string, so that we at least have an xslt result... */ valuePush(ctxt, xmlXPathNewString("")); @@ -392,7 +396,7 @@ void xsl_ext_function_object_php(xmlXPathParserContextPtr ctxt, int nargs) /* {{ /* {{{ proto void xsl_xsltprocessor_import_stylesheet(domdocument doc); URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html# -Since: +Since: */ PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet) { @@ -404,13 +408,13 @@ PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet) xmlNode *nodep = NULL; zend_object_handlers *std_hnd; zval *cloneDocu, *member; - + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oo", &id, xsl_xsltprocessor_class_entry, &docp) == FAILURE) { RETURN_FALSE; } nodep = php_libxml_import_node(docp TSRMLS_CC); - + if (nodep) { doc = nodep->doc; } @@ -419,7 +423,7 @@ PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet) RETURN_FALSE; } - /* libxslt uses _private, so we must copy the imported + /* libxslt uses _private, so we must copy the imported stylesheet document otherwise the node proxies will be a mess */ newdoc = xmlCopyDoc(doc, 1); xmlNodeSetBase((xmlNodePtr) newdoc, (xmlChar *)doc->URL); @@ -436,7 +440,7 @@ PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet) RETURN_FALSE; } - intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC); + intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC); std_hnd = zend_get_std_object_handlers(); MAKE_STD_ZVAL(member); @@ -463,10 +467,10 @@ PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet) intern->hasKeys = clone_docu; } - if ((oldsheetp = (xsltStylesheetPtr)intern->ptr)) { + if ((oldsheetp = (xsltStylesheetPtr)intern->ptr)) { /* free wrapper */ if (((xsltStylesheetPtr) intern->ptr)->_private != NULL) { - ((xsltStylesheetPtr) intern->ptr)->_private = NULL; + ((xsltStylesheetPtr) intern->ptr)->_private = NULL; } xsltFreeStylesheet((xsltStylesheetPtr) intern->ptr); intern->ptr = NULL; @@ -494,7 +498,7 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl xsltSecurityPrefsPtr secPrefs = NULL; node = php_libxml_import_node(docp TSRMLS_CC); - + if (node) { doc = node->doc; } @@ -507,7 +511,7 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl php_error_docref(NULL TSRMLS_CC, E_WARNING, "No stylesheet associated to this object"); return NULL; } - + if (intern->profiling) { if (php_check_open_basedir(intern->profiling TSRMLS_CC)) { f = NULL; @@ -517,7 +521,7 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl } else { f = NULL; } - + if (intern->parameter) { params = php_xsl_xslt_make_params(intern->parameter, 0 TSRMLS_CC); } @@ -549,7 +553,7 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl efree(member); secPrefsValue = intern->securityPrefs; - + /* This whole if block can be removed, when we remove the xsl.security_prefs php.ini option in PHP 6+ */ secPrefsIni= INI_INT("xsl.security_prefs"); /* if secPrefsIni has the same value as secPrefsValue, all is fine */ @@ -569,38 +573,38 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl /* if securityPrefs is set to NONE, we don't have to do any checks, but otherwise... */ if (secPrefsValue != XSL_SECPREF_NONE) { - secPrefs = xsltNewSecurityPrefs(); - if (secPrefsValue & XSL_SECPREF_READ_FILE ) { - if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_READ_FILE, xsltSecurityForbid)) { + secPrefs = xsltNewSecurityPrefs(); + if (secPrefsValue & XSL_SECPREF_READ_FILE ) { + if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_READ_FILE, xsltSecurityForbid)) { secPrefsError = 1; } } - if (secPrefsValue & XSL_SECPREF_WRITE_FILE ) { - if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid)) { + if (secPrefsValue & XSL_SECPREF_WRITE_FILE ) { + if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid)) { secPrefsError = 1; } } - if (secPrefsValue & XSL_SECPREF_CREATE_DIRECTORY ) { - if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid)) { + if (secPrefsValue & XSL_SECPREF_CREATE_DIRECTORY ) { + if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid)) { secPrefsError = 1; } } - if (secPrefsValue & XSL_SECPREF_READ_NETWORK) { - if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_READ_NETWORK, xsltSecurityForbid)) { + if (secPrefsValue & XSL_SECPREF_READ_NETWORK) { + if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_READ_NETWORK, xsltSecurityForbid)) { secPrefsError = 1; } } - if (secPrefsValue & XSL_SECPREF_WRITE_NETWORK) { - if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid)) { + if (secPrefsValue & XSL_SECPREF_WRITE_NETWORK) { + if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid)) { secPrefsError = 1; } } - - if (0 != xsltSetCtxtSecurityPrefs(secPrefs, ctxt)) { + + if (0 != xsltSetCtxtSecurityPrefs(secPrefs, ctxt)) { secPrefsError = 1; } } - + if (secPrefsError == 1) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't set libxslt security properties, not doing transformation for security reasons"); } else { @@ -609,7 +613,7 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl if (f) { fclose(f); } - + xsltFreeTransformContext(ctxt); if (secPrefs) { xsltFreeSecurityPrefs(secPrefs); @@ -617,7 +621,7 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl if (intern->node_list != NULL) { zend_hash_destroy(intern->node_list); - FREE_HASHTABLE(intern->node_list); + FREE_HASHTABLE(intern->node_list); intern->node_list = NULL; } @@ -640,7 +644,7 @@ static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStyl /* {{{ proto domdocument xsl_xsltprocessor_transform_to_doc(domnode doc); URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html# -Since: +Since: */ PHP_FUNCTION(xsl_xsltprocessor_transform_to_doc) { @@ -677,13 +681,13 @@ PHP_FUNCTION(xsl_xsltprocessor_transform_to_doc) found = zend_lookup_class(ret_class, ret_class_len, &ce TSRMLS_CC); if ((found != SUCCESS) || !instanceof_function(*ce, curce TSRMLS_CC)) { xmlFreeDoc(newdocp); - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expecting class compatible with %s, '%s' given", curclass_name, ret_class); RETURN_FALSE; } object_init_ex(return_value, *ce); - + interndoc = (php_libxml_node_object *)zend_objects_get_address(return_value TSRMLS_CC); php_libxml_increment_doc_ref(interndoc, newdocp TSRMLS_CC); php_libxml_increment_node_ptr(interndoc, (xmlNodePtr)newdocp, (void *)interndoc TSRMLS_CC); @@ -693,7 +697,7 @@ PHP_FUNCTION(xsl_xsltprocessor_transform_to_doc) } else { RETURN_FALSE; } - + } /* }}} end xsl_xsltprocessor_transform_to_doc */ @@ -707,7 +711,7 @@ PHP_FUNCTION(xsl_xsltprocessor_transform_to_uri) int ret, uri_len; char *uri; xsl_object *intern; - + id = getThis(); intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC); sheetp = (xsltStylesheetPtr) intern->ptr; @@ -739,7 +743,7 @@ PHP_FUNCTION(xsl_xsltprocessor_transform_to_xml) xmlChar *doc_txt_ptr; int doc_txt_len; xsl_object *intern; - + id = getThis(); intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC); sheetp = (xsltStylesheetPtr) intern->ptr; @@ -770,7 +774,7 @@ PHP_FUNCTION(xsl_xsltprocessor_transform_to_xml) */ PHP_FUNCTION(xsl_xsltprocessor_set_parameter) { - + zval *id; zval *array_value, **entry, *new_string; xsl_object *intern; @@ -786,34 +790,34 @@ PHP_FUNCTION(xsl_xsltprocessor_set_parameter) while (zend_hash_get_current_data(Z_ARRVAL_P(array_value), (void **)&entry) == SUCCESS) { SEPARATE_ZVAL(entry); convert_to_string_ex(entry); - + if (zend_hash_get_current_key_ex(Z_ARRVAL_P(array_value), &string_key, &string_key_len, &idx, 0, NULL) != HASH_KEY_IS_STRING) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter array"); RETURN_FALSE; } - + ALLOC_ZVAL(new_string); Z_ADDREF_PP(entry); COPY_PZVAL_TO_ZVAL(*new_string, *entry); - + zend_hash_update(intern->parameter, string_key, string_key_len, &new_string, sizeof(zval*), NULL); zend_hash_move_forward(Z_ARRVAL_P(array_value)); } RETURN_TRUE; } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sss", &namespace, &namespace_len, &name, &name_len, &value, &value_len) == SUCCESS) { - + intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC); - + MAKE_STD_ZVAL(new_string); ZVAL_STRING(new_string, value, 1); - + zend_hash_update(intern->parameter, name, name_len + 1, &new_string, sizeof(zval*), NULL); RETURN_TRUE; } else { WRONG_PARAM_COUNT; } - + } /* }}} end xsl_xsltprocessor_set_parameter */ @@ -828,7 +832,7 @@ PHP_FUNCTION(xsl_xsltprocessor_get_parameter) xsl_object *intern; DOM_GET_THIS(id); - + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &namespace, &namespace_len, &name, &name_len) == FAILURE) { RETURN_FALSE; } @@ -852,7 +856,7 @@ PHP_FUNCTION(xsl_xsltprocessor_remove_parameter) xsl_object *intern; DOM_GET_THIS(id); - + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &namespace, &namespace_len, &name, &name_len) == FAILURE) { RETURN_FALSE; } @@ -876,7 +880,7 @@ PHP_FUNCTION(xsl_xsltprocessor_register_php_functions) char *name; DOM_GET_THIS(id); - + if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "a", &array_value) == SUCCESS) { intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC); zend_hash_internal_pointer_reset(Z_ARRVAL_P(array_value)); @@ -884,10 +888,10 @@ PHP_FUNCTION(xsl_xsltprocessor_register_php_functions) while (zend_hash_get_current_data(Z_ARRVAL_P(array_value), (void **)&entry) == SUCCESS) { SEPARATE_ZVAL(entry); convert_to_string_ex(entry); - + MAKE_STD_ZVAL(new_string); ZVAL_LONG(new_string,1); - + zend_hash_update(intern->registered_phpfunctions, Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &new_string, sizeof(zval*), NULL); zend_hash_move_forward(Z_ARRVAL_P(array_value)); } @@ -895,17 +899,17 @@ PHP_FUNCTION(xsl_xsltprocessor_register_php_functions) } else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == SUCCESS) { intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC); - + MAKE_STD_ZVAL(new_string); ZVAL_LONG(new_string,1); zend_hash_update(intern->registered_phpfunctions, name, name_len + 1, &new_string, sizeof(zval*), NULL); intern->registerPhpFunctions = 2; - + } else { intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC); intern->registerPhpFunctions = 1; } - + } /* }}} end xsl_xsltprocessor_register_php_functions(); */ @@ -947,7 +951,7 @@ PHP_FUNCTION(xsl_xsltprocessor_set_security_prefs) return; } intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC); - oldSecurityPrefs = intern->securityPrefs; + oldSecurityPrefs = intern->securityPrefs; intern->securityPrefs = securityPrefs; /* set this to 1 so that we know, it was set through this method. Can be removed, when we remove the ini setting */ intern->securityPrefsSet = 1; -- cgit v1.2.1 From df4bf28f9f104ca3ef78ed94b497859f15b004e5 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Sun, 23 Aug 2015 13:27:59 -0700 Subject: Fix bug #70219 (Use after free vulnerability in session deserializer) --- ext/session/session.c | 36 +- ext/session/tests/session_decode_error2.phpt | 518 +++++------------------ ext/session/tests/session_decode_variation3.phpt | 2 +- ext/standard/tests/serialize/bug70219.phpt | 38 ++ ext/standard/var_unserializer.c | 68 +-- ext/standard/var_unserializer.re | 64 +-- 6 files changed, 228 insertions(+), 498 deletions(-) create mode 100644 ext/standard/tests/serialize/bug70219.phpt diff --git a/ext/session/session.c b/ext/session/session.c index 306aba3a7d..0e53c62133 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -210,16 +210,18 @@ static char *php_session_encode(int *newlen TSRMLS_DC) /* {{{ */ } /* }}} */ -static void php_session_decode(const char *val, int vallen TSRMLS_DC) /* {{{ */ +static int php_session_decode(const char *val, int vallen TSRMLS_DC) /* {{{ */ { if (!PS(serializer)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown session.serialize_handler. Failed to decode session object"); - return; + return FAILURE; } if (PS(serializer)->decode(val, vallen TSRMLS_CC) == FAILURE) { php_session_destroy(TSRMLS_C); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to decode session object. Session has been destroyed"); + return FAILURE; } + return SUCCESS; } /* }}} */ @@ -413,7 +415,7 @@ PHPAPI char *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */ php_error_docref(NULL TSRMLS_CC, E_WARNING, "The ini setting hash_bits_per_character is out of range (should be 4, 5, or 6) - using 4 for now"); } - + outid = emalloc((size_t)((digest_len + 2) * ((8.0f / PS(hash_bits_per_character)) + 0.5))); j = (int) (bin_to_readable((char *)digest, digest_len, outid, (char)PS(hash_bits_per_character)) - outid); efree(digest); @@ -855,8 +857,11 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */ ALLOC_INIT_ZVAL(current); if (php_var_unserialize(¤t, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) { php_set_session_var(name, namelen, current, &var_hash TSRMLS_CC); + } else { + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + return FAILURE; } - zval_ptr_dtor(¤t); + var_push_dtor_no_addref(&var_hash, ¤t); } PS_ADD_VARL(name, namelen); efree(name); @@ -947,8 +952,13 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */ ALLOC_INIT_ZVAL(current); if (php_var_unserialize(¤t, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) { php_set_session_var(name, namelen, current, &var_hash TSRMLS_CC); + } else { + var_push_dtor_no_addref(&var_hash, ¤t); + efree(name); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + return FAILURE; } - zval_ptr_dtor(¤t); + var_push_dtor_no_addref(&var_hash, ¤t); } PS_ADD_VARL(name, namelen); skip: @@ -1744,7 +1754,7 @@ static PHP_FUNCTION(session_set_save_handler) } efree(name); } - + if (PS(mod) && PS(mod) != &ps_mod_user) { zend_alter_ini_entry("session.save_handler", sizeof("session.save_handler"), "user", sizeof("user")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); } @@ -1922,9 +1932,7 @@ static PHP_FUNCTION(session_decode) return; } - php_session_decode(str, str_len TSRMLS_CC); - - RETURN_TRUE; + RETVAL_BOOL(php_session_decode(str, str_len TSRMLS_CC) == SUCCESS); } /* }}} */ @@ -2516,12 +2524,12 @@ static int php_session_rfc1867_callback(unsigned int event, void *event_data, vo case MULTIPART_EVENT_FILE_START: { multipart_event_file_start *data = (multipart_event_file_start *) event_data; - /* Do nothing when $_POST["PHP_SESSION_UPLOAD_PROGRESS"] is not set + /* Do nothing when $_POST["PHP_SESSION_UPLOAD_PROGRESS"] is not set * or when we have no session id */ if (!Z_TYPE(progress->sid) || !progress->key.c) { break; } - + /* First FILE_START event, initializing data */ if (!progress->data) { @@ -2571,7 +2579,7 @@ static int php_session_rfc1867_callback(unsigned int event, void *event_data, vo add_assoc_zval_ex(progress->current_file, "bytes_processed", sizeof("bytes_processed"), progress->current_file_bytes_processed); add_next_index_zval(progress->files, progress->current_file); - + Z_LVAL_P(progress->post_bytes_processed) = data->post_bytes_processed; php_session_rfc1867_update(progress, 0 TSRMLS_CC); @@ -2583,7 +2591,7 @@ static int php_session_rfc1867_callback(unsigned int event, void *event_data, vo if (!Z_TYPE(progress->sid) || !progress->key.c) { break; } - + Z_LVAL_P(progress->current_file_bytes_processed) = data->offset + data->length; Z_LVAL_P(progress->post_bytes_processed) = data->post_bytes_processed; @@ -2596,7 +2604,7 @@ static int php_session_rfc1867_callback(unsigned int event, void *event_data, vo if (!Z_TYPE(progress->sid) || !progress->key.c) { break; } - + if (data->temp_filename) { add_assoc_string_ex(progress->current_file, "tmp_name", sizeof("tmp_name"), data->temp_filename, 1); } diff --git a/ext/session/tests/session_decode_error2.phpt b/ext/session/tests/session_decode_error2.phpt index 4160f87855..515047b675 100644 --- a/ext/session/tests/session_decode_error2.phpt +++ b/ext/session/tests/session_decode_error2.phpt @@ -53,563 +53,247 @@ array(0) { } -- Iteration 4 -- -bool(true) -array(1) { - ["foo"]=> - NULL + +Warning: session_decode(): Failed to decode session object. Session has been destroyed in %s/session_decode_error2.php on line %d +bool(false) +array(0) { } -- Iteration 5 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 6 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 7 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 8 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 9 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 10 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 11 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 12 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 13 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 14 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 15 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 16 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 17 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 18 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 19 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 20 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 21 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 22 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 23 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 24 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 25 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 26 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 27 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 28 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 29 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 30 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 31 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 32 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 33 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 34 -- -bool(true) -array(1) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 35 -- -bool(true) -array(1) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 36 -- -bool(true) -array(1) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 37 -- -bool(true) -array(1) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 38 -- -bool(true) -array(1) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 39 -- -bool(true) -array(2) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - NULL +bool(false) +array(0) { } -- Iteration 40 -- -bool(true) -array(2) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - NULL +bool(false) +array(0) { } -- Iteration 41 -- -bool(true) -array(2) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - NULL +bool(false) +array(0) { } -- Iteration 42 -- -bool(true) -array(2) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - NULL +bool(false) +array(0) { } -- Iteration 43 -- -bool(true) -array(2) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 44 -- -bool(true) -array(2) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 45 -- -bool(true) -array(2) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 46 -- -bool(true) -array(2) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 47 -- -bool(true) -array(2) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 48 -- -bool(true) -array(3) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["blah"]=> - NULL +bool(false) +array(0) { } -- Iteration 49 -- -bool(true) -array(3) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["blah"]=> - NULL +bool(false) +array(0) { } -- Iteration 50 -- -bool(true) -array(3) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["blah"]=> - NULL +bool(false) +array(0) { } -- Iteration 51 -- -bool(true) -array(3) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["blah"]=> - NULL +bool(false) +array(0) { } -bool(true) -Done +Warning: session_destroy(): Trying to destroy uninitialized session in %s/session_decode_error2.php on line %d +bool(false) +Done diff --git a/ext/session/tests/session_decode_variation3.phpt b/ext/session/tests/session_decode_variation3.phpt index 4a6f768713..096053171d 100644 --- a/ext/session/tests/session_decode_variation3.phpt +++ b/ext/session/tests/session_decode_variation3.phpt @@ -49,7 +49,7 @@ array(3) { } Warning: session_decode(): Unknown session.serialize_handler. Failed to decode session object in %s on line %d -bool(true) +bool(false) array(3) { ["foo"]=> int(1234567890) diff --git a/ext/standard/tests/serialize/bug70219.phpt b/ext/standard/tests/serialize/bug70219.phpt new file mode 100644 index 0000000000..84a059f365 --- /dev/null +++ b/ext/standard/tests/serialize/bug70219.phpt @@ -0,0 +1,38 @@ +--TEST-- +Bug #70219 Use after free vulnerability in session deserializer +--FILE-- +data); + } + function unserialize($data) { + session_start(); + session_decode($data); + } +} + +$inner = 'ryat|a:1:{i:0;a:1:{i:1;'; +$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:4;}'; + +$data = unserialize($exploit); + +for ($i = 0; $i < 5; $i++) { + $v[$i] = 'hi'.$i; +} + +var_dump($data); +?> +--EXPECTF-- +Warning: session_decode(): Failed to decode session object. Session has been destroyed in %s on line %d +array(2) { + [0]=> + object(obj)#%d (1) { + ["data"]=> + NULL + } + [1]=> + array(0) { + } +} diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index ee0cac4762..ffaf680c51 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.7.5 on Tue Mar 17 13:14:30 2015 */ +/* Generated by re2c 0.13.7.5 on Sun Aug 23 19:50:03 2015 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -92,7 +92,13 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rval) { - var_entries *var_hash = (*var_hashx)->last_dtor; + var_entries *var_hash; + + if (!var_hashx || !*var_hashx) { + return; + } + + var_hash = (*var_hashx)->last_dtor; #if VAR_ENTRIES_DBG fprintf(stderr, "var_push_dtor_no_addref(%ld): %d (%d)\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval)); #endif @@ -121,7 +127,7 @@ PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **n #if VAR_ENTRIES_DBG fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(nzval)); #endif - + while (var_hash) { for (i = 0; i < var_hash->used_slots; i++) { if (var_hash->data[i] == ozval) { @@ -139,7 +145,7 @@ static int var_access(php_unserialize_data_t *var_hashx, long id, zval ***store) #if VAR_ENTRIES_DBG fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id); #endif - + while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) { var_hash = var_hash->next; id -= VAR_ENTRIES_MAX; @@ -162,7 +168,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) #if VAR_ENTRIES_DBG fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L); #endif - + while (var_hash) { next = var_hash->next; efree(var_hash); @@ -170,7 +176,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) } var_hash = (*var_hashx)->first_dtor; - + while (var_hash) { for (i = 0; i < var_hash->used_slots; i++) { zval_ptr_dtor(&var_hash->data[i]); @@ -233,7 +239,7 @@ static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen #define YYMARKER marker -#line 241 "ext/standard/var_unserializer.re" +#line 247 "ext/standard/var_unserializer.re" @@ -251,7 +257,7 @@ static inline long parse_iv2(const unsigned char *p, const unsigned char **q) case '+': p++; } - + while (1) { cursor = (char)*p; if (cursor >= '0' && cursor <= '9') { @@ -280,7 +286,7 @@ static inline size_t parse_uiv(const unsigned char *p) if (*p == '+') { p++; } - + while (1) { cursor = *p; if (cursor >= '0' && cursor <= '9') { @@ -304,24 +310,20 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long ALLOC_INIT_ZVAL(key); if (!php_var_unserialize(&key, p, max, NULL TSRMLS_CC)) { - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); return 0; } if (Z_TYPE_P(key) != IS_LONG && Z_TYPE_P(key) != IS_STRING) { - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); return 0; } ALLOC_INIT_ZVAL(data); if (!php_var_unserialize(&data, p, max, var_hash TSRMLS_CC)) { - zval_dtor(key); - FREE_ZVAL(key); - zval_dtor(data); - FREE_ZVAL(data); + var_push_dtor_no_addref(var_hash, &key); + var_push_dtor_no_addref(var_hash, &data); return 0; } @@ -350,9 +352,7 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long sizeof data, NULL); } var_push_dtor(var_hash, &data); - - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); if (elements && *(*p-1) != ';' && *(*p-1) != '}') { (*p)--; @@ -402,11 +402,11 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) { long elements; - + elements = parse_iv2((*p) + 2, p); (*p) += 2; - + /* The internal class check here is a BC fix only, userspace classes implementing the Serializable interface have eventually an inconsistent behavior at this place when unserialized from a manipulated string. Additionaly the interal classes can possibly @@ -470,19 +470,19 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) limit = max; cursor = *p; - + if (YYCURSOR >= YYLIMIT) { return 0; } - + if (var_hash && cursor[0] != 'R') { var_push(var_hash, rval); } start = cursor; - - + + #line 488 "ext/standard/var_unserializer.c" { @@ -645,7 +645,7 @@ yy20: if (*start == 'C') { custom_object = 1; } - + INIT_PZVAL(*rval); len2 = len = parse_uiv(start + 2); maxlen = max - YYCURSOR; @@ -694,14 +694,14 @@ yy20: efree(class_name); return 0; } - + /* Check for unserialize callback */ if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { incomplete_class = 1; ce = PHP_IC_ENTRY; break; } - + /* Call unserialize callback */ MAKE_STD_ZVAL(user_func); ZVAL_STRING(user_func, PG(unserialize_callback_func), 1); @@ -734,7 +734,7 @@ yy20: zval_ptr_dtor(&arg_func_name); return 0; } - + /* The callback function may have defined the class */ if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) { ce = *pce; @@ -762,7 +762,7 @@ yy20: efree(class_name); return ret; } - + elements = object_common1(UNSERIALIZE_PASSTHRU, ce); if (incomplete_class) { @@ -801,7 +801,7 @@ yy27: { INIT_PZVAL(*rval); - + return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } @@ -1239,7 +1239,7 @@ yy91: *rval = *rval_ref; Z_ADDREF_PP(rval); Z_UNSET_ISREF_PP(rval); - + return 1; } #line 1246 "ext/standard/var_unserializer.c" @@ -1283,7 +1283,7 @@ yy97: *rval = *rval_ref; Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); - + return 1; } #line 1290 "ext/standard/var_unserializer.c" diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index abac77ccea..f02602cd7e 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -90,7 +90,13 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rval) { - var_entries *var_hash = (*var_hashx)->last_dtor; + var_entries *var_hash; + + if (!var_hashx || !*var_hashx) { + return; + } + + var_hash = (*var_hashx)->last_dtor; #if VAR_ENTRIES_DBG fprintf(stderr, "var_push_dtor_no_addref(%ld): %d (%d)\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval)); #endif @@ -119,7 +125,7 @@ PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **n #if VAR_ENTRIES_DBG fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(nzval)); #endif - + while (var_hash) { for (i = 0; i < var_hash->used_slots; i++) { if (var_hash->data[i] == ozval) { @@ -137,7 +143,7 @@ static int var_access(php_unserialize_data_t *var_hashx, long id, zval ***store) #if VAR_ENTRIES_DBG fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id); #endif - + while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) { var_hash = var_hash->next; id -= VAR_ENTRIES_MAX; @@ -160,7 +166,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) #if VAR_ENTRIES_DBG fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L); #endif - + while (var_hash) { next = var_hash->next; efree(var_hash); @@ -168,7 +174,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) } var_hash = (*var_hashx)->first_dtor; - + while (var_hash) { for (i = 0; i < var_hash->used_slots; i++) { zval_ptr_dtor(&var_hash->data[i]); @@ -255,7 +261,7 @@ static inline long parse_iv2(const unsigned char *p, const unsigned char **q) case '+': p++; } - + while (1) { cursor = (char)*p; if (cursor >= '0' && cursor <= '9') { @@ -284,7 +290,7 @@ static inline size_t parse_uiv(const unsigned char *p) if (*p == '+') { p++; } - + while (1) { cursor = *p; if (cursor >= '0' && cursor <= '9') { @@ -308,24 +314,20 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long ALLOC_INIT_ZVAL(key); if (!php_var_unserialize(&key, p, max, NULL TSRMLS_CC)) { - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); return 0; } if (Z_TYPE_P(key) != IS_LONG && Z_TYPE_P(key) != IS_STRING) { - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); return 0; } ALLOC_INIT_ZVAL(data); if (!php_var_unserialize(&data, p, max, var_hash TSRMLS_CC)) { - zval_dtor(key); - FREE_ZVAL(key); - zval_dtor(data); - FREE_ZVAL(data); + var_push_dtor_no_addref(var_hash, &key); + var_push_dtor_no_addref(var_hash, &data); return 0; } @@ -354,9 +356,7 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long sizeof data, NULL); } var_push_dtor(var_hash, &data); - - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); if (elements && *(*p-1) != ';' && *(*p-1) != '}') { (*p)--; @@ -406,11 +406,11 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) { long elements; - + elements = parse_iv2((*p) + 2, p); (*p) += 2; - + /* The internal class check here is a BC fix only, userspace classes implementing the Serializable interface have eventually an inconsistent behavior at this place when unserialized from a manipulated string. Additionaly the interal classes can possibly @@ -474,19 +474,19 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) limit = max; cursor = *p; - + if (YYCURSOR >= YYLIMIT) { return 0; } - + if (var_hash && cursor[0] != 'R') { var_push(var_hash, rval); } start = cursor; - - + + /*!re2c "R:" iv ";" { @@ -506,7 +506,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) *rval = *rval_ref; Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); - + return 1; } @@ -529,7 +529,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) *rval = *rval_ref; Z_ADDREF_PP(rval); Z_UNSET_ISREF_PP(rval); - + return 1; } @@ -679,7 +679,7 @@ use_double: "o:" iv ":" ["] { INIT_PZVAL(*rval); - + return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } @@ -702,7 +702,7 @@ object ":" uiv ":" ["] { if (*start == 'C') { custom_object = 1; } - + INIT_PZVAL(*rval); len2 = len = parse_uiv(start + 2); maxlen = max - YYCURSOR; @@ -751,14 +751,14 @@ object ":" uiv ":" ["] { efree(class_name); return 0; } - + /* Check for unserialize callback */ if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { incomplete_class = 1; ce = PHP_IC_ENTRY; break; } - + /* Call unserialize callback */ MAKE_STD_ZVAL(user_func); ZVAL_STRING(user_func, PG(unserialize_callback_func), 1); @@ -791,7 +791,7 @@ object ":" uiv ":" ["] { zval_ptr_dtor(&arg_func_name); return 0; } - + /* The callback function may have defined the class */ if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) { ce = *pce; @@ -819,7 +819,7 @@ object ":" uiv ":" ["] { efree(class_name); return ret; } - + elements = object_common1(UNSERIALIZE_PASSTHRU, ce); if (incomplete_class) { -- cgit v1.2.1 From 64043cb9e5d8bc5af719678893e38ee0290e0c0a Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Fri, 28 Aug 2015 22:25:41 -0700 Subject: Fix bug #70385 (Buffer over-read in exif_read_data with TIFF IFD tag byte value of 32 bytes) --- ext/exif/exif.c | 208 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 7f95ff43ea..43e68c449e 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -74,7 +74,7 @@ #include "php_ini.h" #include "ext/standard/php_string.h" #include "ext/standard/php_image.h" -#include "ext/standard/info.h" +#include "ext/standard/info.h" /* needed for ssize_t definition */ #include @@ -161,7 +161,7 @@ ZEND_BEGIN_MODULE_GLOBALS(exif) char * encode_jis; char * decode_jis_be; char * decode_jis_le; -ZEND_END_MODULE_GLOBALS(exif) +ZEND_END_MODULE_GLOBALS(exif) ZEND_DECLARE_MODULE_GLOBALS(exif) @@ -170,7 +170,7 @@ ZEND_DECLARE_MODULE_GLOBALS(exif) #else #define EXIF_G(v) (exif_globals.v) #endif - + /* {{{ PHP_INI */ @@ -213,7 +213,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("exif.decode_jis_intel", "JIS", PHP_INI_ALL, OnUpdateDecode, decode_jis_le, zend_exif_globals, exif_globals) PHP_INI_END() /* }}} */ - + /* {{{ PHP_GINIT_FUNCTION */ static PHP_GINIT_FUNCTION(exif) @@ -233,9 +233,9 @@ PHP_MINIT_FUNCTION(exif) { REGISTER_INI_ENTRIES(); if (zend_hash_exists(&module_registry, "mbstring", sizeof("mbstring"))) { - REGISTER_LONG_CONSTANT("EXIF_USE_MBSTRING", 1, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("EXIF_USE_MBSTRING", 1, CONST_CS | CONST_PERSISTENT); } else { - REGISTER_LONG_CONSTANT("EXIF_USE_MBSTRING", 0, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("EXIF_USE_MBSTRING", 0, CONST_CS | CONST_PERSISTENT); } return SUCCESS; } @@ -265,7 +265,7 @@ zend_module_entry exif_module_entry = { exif_module_deps, "exif", exif_functions, - PHP_MINIT(exif), + PHP_MINIT(exif), PHP_MSHUTDOWN(exif), NULL, NULL, PHP_MINFO(exif), @@ -278,7 +278,7 @@ zend_module_entry exif_module_entry = { NULL, NULL, STANDARD_MODULE_PROPERTIES_EX -#else +#else STANDARD_MODULE_PROPERTIES #endif }; @@ -667,73 +667,73 @@ static tag_info_array tag_table_IFD = { { 0x0213, "YCbCrPositioning"}, { 0x0214, "ReferenceBlackWhite"}, { 0x02BC, "ExtensibleMetadataPlatform"}, /* XAP: Extensible Authoring Publishing, obsoleted by XMP: Extensible Metadata Platform */ - { 0x0301, "Gamma"}, - { 0x0302, "ICCProfileDescriptor"}, - { 0x0303, "SRGBRenderingIntent"}, - { 0x0320, "ImageTitle"}, - { 0x5001, "ResolutionXUnit"}, - { 0x5002, "ResolutionYUnit"}, - { 0x5003, "ResolutionXLengthUnit"}, - { 0x5004, "ResolutionYLengthUnit"}, - { 0x5005, "PrintFlags"}, - { 0x5006, "PrintFlagsVersion"}, - { 0x5007, "PrintFlagsCrop"}, - { 0x5008, "PrintFlagsBleedWidth"}, - { 0x5009, "PrintFlagsBleedWidthScale"}, - { 0x500A, "HalftoneLPI"}, - { 0x500B, "HalftoneLPIUnit"}, - { 0x500C, "HalftoneDegree"}, - { 0x500D, "HalftoneShape"}, - { 0x500E, "HalftoneMisc"}, - { 0x500F, "HalftoneScreen"}, - { 0x5010, "JPEGQuality"}, - { 0x5011, "GridSize"}, - { 0x5012, "ThumbnailFormat"}, - { 0x5013, "ThumbnailWidth"}, - { 0x5014, "ThumbnailHeight"}, - { 0x5015, "ThumbnailColorDepth"}, - { 0x5016, "ThumbnailPlanes"}, - { 0x5017, "ThumbnailRawBytes"}, - { 0x5018, "ThumbnailSize"}, - { 0x5019, "ThumbnailCompressedSize"}, - { 0x501A, "ColorTransferFunction"}, - { 0x501B, "ThumbnailData"}, - { 0x5020, "ThumbnailImageWidth"}, - { 0x5021, "ThumbnailImageHeight"}, - { 0x5022, "ThumbnailBitsPerSample"}, - { 0x5023, "ThumbnailCompression"}, - { 0x5024, "ThumbnailPhotometricInterp"}, - { 0x5025, "ThumbnailImageDescription"}, - { 0x5026, "ThumbnailEquipMake"}, - { 0x5027, "ThumbnailEquipModel"}, - { 0x5028, "ThumbnailStripOffsets"}, - { 0x5029, "ThumbnailOrientation"}, - { 0x502A, "ThumbnailSamplesPerPixel"}, - { 0x502B, "ThumbnailRowsPerStrip"}, - { 0x502C, "ThumbnailStripBytesCount"}, - { 0x502D, "ThumbnailResolutionX"}, - { 0x502E, "ThumbnailResolutionY"}, - { 0x502F, "ThumbnailPlanarConfig"}, - { 0x5030, "ThumbnailResolutionUnit"}, - { 0x5031, "ThumbnailTransferFunction"}, - { 0x5032, "ThumbnailSoftwareUsed"}, - { 0x5033, "ThumbnailDateTime"}, - { 0x5034, "ThumbnailArtist"}, - { 0x5035, "ThumbnailWhitePoint"}, - { 0x5036, "ThumbnailPrimaryChromaticities"}, - { 0x5037, "ThumbnailYCbCrCoefficients"}, - { 0x5038, "ThumbnailYCbCrSubsampling"}, - { 0x5039, "ThumbnailYCbCrPositioning"}, - { 0x503A, "ThumbnailRefBlackWhite"}, - { 0x503B, "ThumbnailCopyRight"}, - { 0x5090, "LuminanceTable"}, - { 0x5091, "ChrominanceTable"}, - { 0x5100, "FrameDelay"}, - { 0x5101, "LoopCount"}, - { 0x5110, "PixelUnit"}, - { 0x5111, "PixelPerUnitX"}, - { 0x5112, "PixelPerUnitY"}, - { 0x5113, "PaletteHistogram"}, + { 0x0301, "Gamma"}, + { 0x0302, "ICCProfileDescriptor"}, + { 0x0303, "SRGBRenderingIntent"}, + { 0x0320, "ImageTitle"}, + { 0x5001, "ResolutionXUnit"}, + { 0x5002, "ResolutionYUnit"}, + { 0x5003, "ResolutionXLengthUnit"}, + { 0x5004, "ResolutionYLengthUnit"}, + { 0x5005, "PrintFlags"}, + { 0x5006, "PrintFlagsVersion"}, + { 0x5007, "PrintFlagsCrop"}, + { 0x5008, "PrintFlagsBleedWidth"}, + { 0x5009, "PrintFlagsBleedWidthScale"}, + { 0x500A, "HalftoneLPI"}, + { 0x500B, "HalftoneLPIUnit"}, + { 0x500C, "HalftoneDegree"}, + { 0x500D, "HalftoneShape"}, + { 0x500E, "HalftoneMisc"}, + { 0x500F, "HalftoneScreen"}, + { 0x5010, "JPEGQuality"}, + { 0x5011, "GridSize"}, + { 0x5012, "ThumbnailFormat"}, + { 0x5013, "ThumbnailWidth"}, + { 0x5014, "ThumbnailHeight"}, + { 0x5015, "ThumbnailColorDepth"}, + { 0x5016, "ThumbnailPlanes"}, + { 0x5017, "ThumbnailRawBytes"}, + { 0x5018, "ThumbnailSize"}, + { 0x5019, "ThumbnailCompressedSize"}, + { 0x501A, "ColorTransferFunction"}, + { 0x501B, "ThumbnailData"}, + { 0x5020, "ThumbnailImageWidth"}, + { 0x5021, "ThumbnailImageHeight"}, + { 0x5022, "ThumbnailBitsPerSample"}, + { 0x5023, "ThumbnailCompression"}, + { 0x5024, "ThumbnailPhotometricInterp"}, + { 0x5025, "ThumbnailImageDescription"}, + { 0x5026, "ThumbnailEquipMake"}, + { 0x5027, "ThumbnailEquipModel"}, + { 0x5028, "ThumbnailStripOffsets"}, + { 0x5029, "ThumbnailOrientation"}, + { 0x502A, "ThumbnailSamplesPerPixel"}, + { 0x502B, "ThumbnailRowsPerStrip"}, + { 0x502C, "ThumbnailStripBytesCount"}, + { 0x502D, "ThumbnailResolutionX"}, + { 0x502E, "ThumbnailResolutionY"}, + { 0x502F, "ThumbnailPlanarConfig"}, + { 0x5030, "ThumbnailResolutionUnit"}, + { 0x5031, "ThumbnailTransferFunction"}, + { 0x5032, "ThumbnailSoftwareUsed"}, + { 0x5033, "ThumbnailDateTime"}, + { 0x5034, "ThumbnailArtist"}, + { 0x5035, "ThumbnailWhitePoint"}, + { 0x5036, "ThumbnailPrimaryChromaticities"}, + { 0x5037, "ThumbnailYCbCrCoefficients"}, + { 0x5038, "ThumbnailYCbCrSubsampling"}, + { 0x5039, "ThumbnailYCbCrPositioning"}, + { 0x503A, "ThumbnailRefBlackWhite"}, + { 0x503B, "ThumbnailCopyRight"}, + { 0x5090, "LuminanceTable"}, + { 0x5091, "ChrominanceTable"}, + { 0x5100, "FrameDelay"}, + { 0x5101, "LoopCount"}, + { 0x5110, "PixelUnit"}, + { 0x5111, "PixelPerUnitX"}, + { 0x5112, "PixelPerUnitY"}, + { 0x5113, "PaletteHistogram"}, { 0x1000, "RelatedImageFileFormat"}, { 0x800D, "ImageID"}, { 0x80E3, "Matteing"}, /* obsoleted by ExtraSamples */ @@ -939,7 +939,7 @@ static tag_info_array tag_table_VND_NIKON = { { 0x000b, "Converter"}, TAG_TABLE_END }; - + static tag_info_array tag_table_VND_NIKON_990 = { { 0x0001, "Version"}, { 0x0002, "ISOSetting"}, @@ -958,7 +958,7 @@ static tag_info_array tag_table_VND_NIKON_990 = { { 0x0010, "DataDump"}, TAG_TABLE_END }; - + static tag_info_array tag_table_VND_OLYMPUS = { { 0x0200, "SpecialMode"}, { 0x0201, "JPEGQuality"}, @@ -1224,7 +1224,7 @@ char * exif_dump_data(int *dump_free, int format, int components, int length, in if (components > 0) { dump = erealloc(dump, len + 2 + 1); snprintf(dump + len, 2 + 1, ", "); - len += 2; + len += 2; components--; } else{ break; @@ -1574,7 +1574,7 @@ typedef struct { static void exif_error_docref(const char *docref EXIFERR_DC, const image_info_type *ImageInfo, int type, const char *format, ...) { va_list args; - + va_start(args, format); #ifdef EXIF_DEBUG { @@ -2627,7 +2627,7 @@ static int exif_process_user_comment(image_info_type *ImageInfo, char **pszInfoP *pszEncoding = estrdup((const char*)szValuePtr); szValuePtr = szValuePtr+8; ByteCount -= 8; - /* First try to detect BOM: ZERO WIDTH NOBREAK SPACE (FEFF 16) + /* First try to detect BOM: ZERO WIDTH NOBREAK SPACE (FEFF 16) * since we have no encoding support for the BOM yet we skip that. */ if (!memcmp(szValuePtr, "\xFE\xFF", 2)) { @@ -2645,8 +2645,8 @@ static int exif_process_user_comment(image_info_type *ImageInfo, char **pszInfoP } /* XXX this will fail again if encoding_converter returns on error something different than SIZE_MAX */ if (zend_multibyte_encoding_converter( - (unsigned char**)pszInfoPtr, - &len, + (unsigned char**)pszInfoPtr, + &len, (unsigned char*)szValuePtr, ByteCount, zend_multibyte_fetch_encoding(ImageInfo->encode_unicode TSRMLS_CC), @@ -2666,8 +2666,8 @@ static int exif_process_user_comment(image_info_type *ImageInfo, char **pszInfoP ByteCount -= 8; /* XXX this will fail again if encoding_converter returns on error something different than SIZE_MAX */ if (zend_multibyte_encoding_converter( - (unsigned char**)pszInfoPtr, - &len, + (unsigned char**)pszInfoPtr, + &len, (unsigned char*)szValuePtr, ByteCount, zend_multibyte_fetch_encoding(ImageInfo->encode_jis TSRMLS_CC), @@ -2701,12 +2701,12 @@ static int exif_process_user_comment(image_info_type *ImageInfo, char **pszInfoP * Process unicode field in IFD. */ static int exif_process_unicode(image_info_type *ImageInfo, xp_field_type *xp_field, int tag, char *szValuePtr, int ByteCount TSRMLS_DC) { - xp_field->tag = tag; + xp_field->tag = tag; xp_field->value = NULL; /* XXX this will fail again if encoding_converter returns on error something different than SIZE_MAX */ if (zend_multibyte_encoding_converter( - (unsigned char**)&xp_field->value, - &xp_field->size, + (unsigned char**)&xp_field->value, + &xp_field->size, (unsigned char*)szValuePtr, ByteCount, zend_multibyte_fetch_encoding(ImageInfo->encode_unicode TSRMLS_CC), @@ -2731,7 +2731,7 @@ static int exif_process_IFD_in_MAKERNOTE(image_info_type *ImageInfo, char * valu if (i==sizeof(maker_note_array)/sizeof(maker_note_type)) return FALSE; maker_note = maker_note_array+i; - + /*exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "check (%s,%s)", maker_note->make?maker_note->make:"", maker_note->model?maker_note->model:"");*/ if (maker_note->make && (!ImageInfo->make || strcmp(maker_note->make, ImageInfo->make))) continue; @@ -2852,9 +2852,9 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha offset_val = php_ifd_get32u(dir_entry+8, ImageInfo->motorola_intel); /* If its bigger than 4 bytes, the dir entry contains an offset. */ value_ptr = offset_base+offset_val; - /* + /* dir_entry is ImageInfo->file.list[sn].data+2+i*12 - offset_base is ImageInfo->file.list[sn].data-dir_offset + offset_base is ImageInfo->file.list[sn].data-dir_offset dir_entry - offset_base is dir_offset+2+i*12 */ if (byte_count > IFDlength || offset_val > IFDlength-byte_count || value_ptr < dir_entry || offset_val < (size_t)(dir_entry-offset_base)) { @@ -2925,18 +2925,18 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha case TAG_COMP_IMAGE_WIDTH: ImageInfo->Thumbnail.width = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC); break; - + case TAG_IMAGEHEIGHT: case TAG_COMP_IMAGE_HEIGHT: ImageInfo->Thumbnail.height = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC); break; - + case TAG_STRIP_OFFSETS: case TAG_JPEG_INTERCHANGE_FORMAT: /* accept both formats */ ImageInfo->Thumbnail.offset = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC); break; - + case TAG_STRIP_BYTE_COUNTS: if (ImageInfo->FileType == IMAGE_FILETYPE_TIFF_II || ImageInfo->FileType == IMAGE_FILETYPE_TIFF_MM) { ImageInfo->Thumbnail.filetype = ImageInfo->FileType; @@ -2946,7 +2946,7 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha } ImageInfo->Thumbnail.size = exif_convert_any_to_int(value_ptr, format, ImageInfo->motorola_intel TSRMLS_CC); break; - + case TAG_JPEG_INTERCHANGE_FORMAT_LEN: if (ImageInfo->Thumbnail.filetype == IMAGE_FILETYPE_UNKNOWN) { ImageInfo->Thumbnail.filetype = IMAGE_FILETYPE_JPEG; @@ -2973,7 +2973,7 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha ImageInfo->Copyright = estrdup(value_ptr); } } - break; + break; case TAG_USERCOMMENT: ImageInfo->UserCommentLength = exif_process_user_comment(ImageInfo, &(ImageInfo->UserComment), &(ImageInfo->UserCommentEncoding), value_ptr, byte_count TSRMLS_CC); @@ -3061,10 +3061,10 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha break; case TAG_MAKE: - ImageInfo->make = estrdup(value_ptr); + ImageInfo->make = estrndup(value_ptr, byte_count); break; case TAG_MODEL: - ImageInfo->model = estrdup(value_ptr); + ImageInfo->model = estrndup(value_ptr, byte_count); break; case TAG_MAKER_NOTE: @@ -3414,7 +3414,7 @@ static int exif_scan_JPEG_header(image_info_type *ImageInfo TSRMLS_DC) if ((itemlen - 2) < 6) { return FALSE; } - + exif_process_SOFn(Data, marker, &sof_info); ImageInfo->Width = sof_info.width; ImageInfo->Height = sof_info.height; @@ -3459,21 +3459,21 @@ static int exif_scan_thumbnail(image_info_type *ImageInfo TSRMLS_DC) } for (;;) { pos += length; - if (pos>=ImageInfo->Thumbnail.size) + if (pos>=ImageInfo->Thumbnail.size) return FALSE; c = data[pos++]; - if (pos>=ImageInfo->Thumbnail.size) + if (pos>=ImageInfo->Thumbnail.size) return FALSE; if (c != 0xFF) { return FALSE; } n = 8; while ((c = data[pos++]) == 0xFF && n--) { - if (pos+3>=ImageInfo->Thumbnail.size) + if (pos+3>=ImageInfo->Thumbnail.size) return FALSE; /* +3 = pos++ of next check when reaching marker + 2 bytes for length */ } - if (c == 0xFF) + if (c == 0xFF) return FALSE; marker = c; length = php_jpg_get16(data+pos); @@ -3787,7 +3787,7 @@ static int exif_scan_FILE_header(image_info_type *ImageInfo TSRMLS_DC) exif_error_docref(NULL EXIFERR_CC, ImageInfo, E_NOTICE, "File has TIFF/II format"); #endif ImageInfo->sections_found |= FOUND_IFD0; - if (exif_process_IFD_in_TIFF(ImageInfo, + if (exif_process_IFD_in_TIFF(ImageInfo, php_ifd_get32u(file_header + 4, ImageInfo->motorola_intel), SECTION_IFD0 TSRMLS_CC)) { ret = TRUE; @@ -3967,7 +3967,7 @@ PHP_FUNCTION(exif_read_data) sections_str = exif_get_sectionlist(ImageInfo.sections_found TSRMLS_CC); #ifdef EXIF_DEBUG - if (sections_str) + if (sections_str) exif_error_docref(NULL EXIFERR_CC, &ImageInfo, E_NOTICE, "Sections found: %s", sections_str[0] ? sections_str : "None"); #endif -- cgit v1.2.1 From 03964892c054d0c736414c10b3edc7a40318b975 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Fri, 28 Aug 2015 22:52:50 -0700 Subject: Fix bug #70345 (Multiple vulnerabilities related to PCRE functions) --- ext/pcre/php_pcre.c | 152 +++++++++++++++++++++---------------------- ext/pcre/tests/bug70345.phpt | 24 +++++++ 2 files changed, 100 insertions(+), 76 deletions(-) create mode 100644 ext/pcre/tests/bug70345.phpt diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c index d7a4309b24..071b1a7dcf 100644 --- a/ext/pcre/php_pcre.c +++ b/ext/pcre/php_pcre.c @@ -136,7 +136,7 @@ static PHP_MINFO_FUNCTION(pcre) static PHP_MINIT_FUNCTION(pcre) { REGISTER_INI_ENTRIES(); - + REGISTER_LONG_CONSTANT("PREG_PATTERN_ORDER", PREG_PATTERN_ORDER, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PREG_SET_ORDER", PREG_SET_ORDER, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("PREG_OFFSET_CAPTURE", PREG_OFFSET_CAPTURE, CONST_CS | CONST_PERSISTENT); @@ -276,18 +276,18 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(char *regex, int regex_le #endif } } - + p = regex; - + /* Parse through the leading whitespace, and display a warning if we get to the end without encountering a delimiter. */ while (isspace((int)*(unsigned char *)p)) p++; if (*p == 0) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, + php_error_docref(NULL TSRMLS_CC, E_WARNING, p < regex + regex_len ? "Null byte in regex" : "Empty regular expression"); return NULL; } - + /* Get the delimiter and display a warning if it is alphanumeric or a backslash. */ delimiter = *p++; @@ -340,7 +340,7 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(char *regex, int regex_le } return NULL; } - + /* Make a copy of the actual pattern. */ pattern = estrndup(p, pp-p); @@ -348,7 +348,7 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(char *regex, int regex_le pp++; /* Parse through the options, setting appropriate flags. Display - a warning if we encounter an unknown modifier. */ + a warning if we encounter an unknown modifier. */ while (pp < regex + regex_len) { switch (*pp++) { /* Perl compatible options */ @@ -356,7 +356,7 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(char *regex, int regex_le case 'm': coptions |= PCRE_MULTILINE; break; case 's': coptions |= PCRE_DOTALL; break; case 'x': coptions |= PCRE_EXTENDED; break; - + /* PCRE specific options */ case 'A': coptions |= PCRE_ANCHORED; break; case 'D': coptions |= PCRE_DOLLAR_ENDONLY;break; @@ -369,12 +369,12 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(char *regex, int regex_le the PCRE_UCP option. */ #ifdef PCRE_UCP coptions |= PCRE_UCP; -#endif +#endif break; /* Custom preg options */ case 'e': poptions |= PREG_REPLACE_EVAL; break; - + case ' ': case '\n': break; @@ -453,7 +453,7 @@ PHPAPI pcre_cache_entry* pcre_get_compiled_regex_cache(char *regex, int regex_le * at end of request. However PCRE_G(pcre_cache) must be consistent * on the next request as well. So we disable usage of interned strings * as hash keys especually for this table. - * See bug #63180 + * See bug #63180 */ if (IS_INTERNED(regex)) { regex = tmp = estrndup(regex, regex_len); @@ -482,7 +482,7 @@ PHPAPI pcre* pcre_get_compiled_regex(char *regex, pcre_extra **extra, int *preg_ if (preg_options) { *preg_options = pce ? pce->preg_options : 0; } - + return pce ? pce->re : NULL; } /* }}} */ @@ -492,7 +492,7 @@ PHPAPI pcre* pcre_get_compiled_regex(char *regex, pcre_extra **extra, int *preg_ PHPAPI pcre* pcre_get_compiled_regex_ex(char *regex, pcre_extra **extra, int *preg_options, int *compile_options TSRMLS_DC) { pcre_cache_entry * pce = pcre_get_compiled_regex_cache(regex, strlen(regex) TSRMLS_CC); - + if (extra) { *extra = pce ? pce->extra : NULL; } @@ -502,7 +502,7 @@ PHPAPI pcre* pcre_get_compiled_regex_ex(char *regex, pcre_extra **extra, int *pr if (compile_options) { *compile_options = pce ? pce->compile_options : 0; } - + return pce ? pce->re : NULL; } /* }}} */ @@ -519,7 +519,7 @@ static inline void add_offset_pair(zval *result, char *str, int len, int offset, /* Add (match, offset) to the return value */ add_next_index_stringl(match_pair, str, len, 1); add_next_index_long(match_pair, offset); - + if (name) { zval_add_ref(&match_pair); zend_hash_update(Z_ARRVAL_P(result), name, strlen(name)+1, &match_pair, sizeof(zval *), NULL); @@ -544,13 +544,13 @@ static void php_do_pcre_match(INTERNAL_FUNCTION_PARAMETERS, int global) /* {{{ * &subject, &subject_len, &subpats, &flags, &start_offset) == FAILURE) { RETURN_FALSE; } - + /* Compile regex or get it from cache. */ if ((pce = pcre_get_compiled_regex_cache(regex, regex_len TSRMLS_CC)) == NULL) { RETURN_FALSE; } - php_pcre_match_impl(pce, subject, subject_len, return_value, subpats, + php_pcre_match_impl(pce, subject, subject_len, return_value, subpats, global, ZEND_NUM_ARGS() >= 4, flags, start_offset TSRMLS_CC); } /* }}} */ @@ -653,7 +653,7 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, char *subject, int subjec matched = 0; PCRE_G(error_code) = PHP_PCRE_NO_ERROR; - + do { /* Execute the regular expression. */ count = pcre_exec(pce->re, extra, subject, subject_len, start_offset, @@ -675,7 +675,7 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, char *subject, int subjec /* If subpatterns array has been passed, fill it in with values. */ if (subpats != NULL) { /* Try to get the list of substrings and display a warning if failed. */ - if (pcre_get_substring_list(subject, offsets, count, &stringlist) < 0) { + if ((offsets[1] - offsets[0] < 0) || pcre_get_substring_list(subject, offsets, count, &stringlist) < 0) { efree(subpat_names); efree(offsets); if (match_sets) efree(match_sets); @@ -710,7 +710,7 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, char *subject, int subjec ALLOC_ZVAL(result_set); array_init(result_set); INIT_PZVAL(result_set); - + /* Add all the subpatterns to it */ for (i = 0; i < count; i++) { if (offset_capture) { @@ -762,13 +762,13 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, char *subject, int subjec pcre_handle_exec_error(count TSRMLS_CC); break; } - + /* If we have matched an empty string, mimic what Perl's /g options does. This turns out to be rather cunning. First we set PCRE_NOTEMPTY and try the match again at the same point. If this fails (picked up above) we advance to the next character. */ g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0; - + /* Advance to the position right after the last full match */ start_offset = offsets[1]; } while (global); @@ -785,7 +785,7 @@ PHPAPI void php_pcre_match_impl(pcre_cache_entry *pce, char *subject, int subjec } efree(match_sets); } - + efree(offsets); efree(subpat_names); @@ -835,7 +835,7 @@ static int preg_get_backref(char **str, int *backref) walk++; } else return 0; - + if (*walk && *walk >= '0' && *walk <= '9') { *backref = *backref * 10 + *walk - '0'; walk++; @@ -847,9 +847,9 @@ static int preg_get_backref(char **str, int *backref) else walk++; } - + *str = walk; - return 1; + return 1; } /* }}} */ @@ -859,7 +859,7 @@ static int preg_do_repl_func(zval *function, char *subject, int *offsets, char * { zval *retval_ptr; /* Function return value */ zval **args[1]; /* Argument to pass to function */ - zval *subpats; /* Captured subpatterns */ + zval *subpats; /* Captured subpatterns */ int result_len; /* Return value length */ int i; @@ -910,11 +910,11 @@ static int preg_do_eval(char *eval_str, int eval_str_len, char *subject, int backref; /* Current backref */ char *compiled_string_description; smart_str code = {0}; - + eval_str_end = eval_str + eval_str_len; walk = segment = eval_str; walk_last = 0; - + while (walk < eval_str_end) { /* If found a backreference.. */ if ('\\' == *walk || '$' == *walk) { @@ -967,15 +967,15 @@ static int preg_do_eval(char *eval_str, int eval_str_len, char *subject, } efree(compiled_string_description); convert_to_string(&retval); - + /* Save the return value and its length */ *result = estrndup(Z_STRVAL(retval), Z_STRLEN(retval)); result_len = Z_STRLEN(retval); - + /* Clean up */ zval_dtor(&retval); smart_str_free(&code); - + return result_len; } /* }}} */ @@ -994,13 +994,13 @@ PHPAPI char *php_pcre_replace(char *regex, int regex_len, return NULL; } - return php_pcre_replace_impl(pce, subject, subject_len, replace_val, + return php_pcre_replace_impl(pce, subject, subject_len, replace_val, is_callable_replace, result_len, limit, replace_count TSRMLS_CC); } /* }}} */ /* {{{ php_pcre_replace_impl() */ -PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *replace_val, +PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int subject_len, zval *replace_val, int is_callable_replace, int *result_len, int limit, int *replace_count TSRMLS_DC) { pcre_extra *extra = pce->extra;/* Holds results of studying */ @@ -1072,7 +1072,7 @@ PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int sub } offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0); - + alloc_len = 2 * subject_len + 1; result = safe_emalloc(alloc_len, sizeof(char), 0); @@ -1081,7 +1081,7 @@ PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int sub *result_len = 0; start_offset = 0; PCRE_G(error_code) = PHP_PCRE_NO_ERROR; - + while (1) { /* Execute the regular expression. */ count = pcre_exec(pce->re, extra, subject, subject_len, start_offset, @@ -1098,7 +1098,7 @@ PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int sub piece = subject + start_offset; - if (count > 0 && (limit == -1 || limit > 0)) { + if (count > 0 && (offsets[1] - offsets[0] >= 0) && (limit == -1 || limit > 0)) { if (replace_count) { ++*replace_count; } @@ -1106,7 +1106,7 @@ PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int sub match = subject + offsets[0]; new_len = *result_len + offsets[0] - start_offset; /* part before the match */ - + /* If evaluating, do it and add the return string's length */ if (eval) { eval_result_len = preg_do_eval(replace, replace_len, subject, @@ -1151,7 +1151,7 @@ PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int sub /* copy replacement and backrefs */ walkbuf = result + *result_len; - + /* If evaluating or using custom function, copy result to the buffer * and clean up. */ if (eval || is_callable_replace) { @@ -1219,13 +1219,13 @@ PHPAPI char *php_pcre_replace_impl(pcre_cache_entry *pce, char *subject, int sub result = NULL; break; } - + /* If we have matched an empty string, mimic what Perl's /g options does. This turns out to be rather cunning. First we set PCRE_NOTEMPTY and try the match again at the same point. If this fails (picked up above) we advance to the next character. */ g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0; - + /* Advance to the next piece. */ start_offset = offsets[1]; } @@ -1249,18 +1249,18 @@ static char *php_replace_in_subject(zval *regex, zval *replace, zval **subject, *result; int subject_len; - /* Make sure we're dealing with strings. */ + /* Make sure we're dealing with strings. */ convert_to_string_ex(subject); /* FIXME: This might need to be changed to STR_EMPTY_ALLOC(). Check if this zval could be dtor()'ed somehow */ ZVAL_STRINGL(&empty_replace, "", 0, 0); - + /* If regex is an array */ if (Z_TYPE_P(regex) == IS_ARRAY) { /* Duplicate subject string for repeated replacement */ subject_value = estrndup(Z_STRVAL_PP(subject), Z_STRLEN_PP(subject)); subject_len = Z_STRLEN_PP(subject); *result_len = subject_len; - + zend_hash_internal_pointer_reset(Z_ARRVAL_P(regex)); replace_value = replace; @@ -1269,9 +1269,9 @@ static char *php_replace_in_subject(zval *regex, zval *replace, zval **subject, /* For each entry in the regex array, get the entry */ while (zend_hash_get_current_data(Z_ARRVAL_P(regex), (void **)®ex_entry) == SUCCESS) { - /* Make sure we're dealing with strings. */ + /* Make sure we're dealing with strings. */ convert_to_string_ex(regex_entry); - + /* If replace is an array and not a callable construct */ if (Z_TYPE_P(replace) == IS_ARRAY && !is_callable_replace) { /* Get current entry */ @@ -1286,7 +1286,7 @@ static char *php_replace_in_subject(zval *regex, zval *replace, zval **subject, replace_value = &empty_replace; } } - + /* Do the actual replacement and put the result back into subject_value for further replacements. */ if ((result = php_pcre_replace(Z_STRVAL_PP(regex_entry), @@ -1342,12 +1342,12 @@ static void preg_replace_impl(INTERNAL_FUNCTION_PARAMETERS, int is_callable_repl ulong num_key; char *callback_name; int replace_count=0, old_replace_count; - + /* Get function parameters and do error-checking. */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZZ|lZ", ®ex, &replace, &subject, &limit, &zcount) == FAILURE) { return; } - + if (!is_callable_replace && Z_TYPE_PP(replace) == IS_ARRAY && Z_TYPE_PP(regex) != IS_ARRAY) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Parameter mismatch, pattern is a string while replacement is an array"); RETURN_FALSE; @@ -1373,10 +1373,10 @@ static void preg_replace_impl(INTERNAL_FUNCTION_PARAMETERS, int is_callable_repl if (ZEND_NUM_ARGS() > 3) { limit_val = limit; } - + if (Z_TYPE_PP(regex) != IS_ARRAY) convert_to_string_ex(regex); - + /* if subject is an array */ if (Z_TYPE_PP(subject) == IS_ARRAY) { array_init(return_value); @@ -1404,7 +1404,7 @@ static void preg_replace_impl(INTERNAL_FUNCTION_PARAMETERS, int is_callable_repl efree(result); } } - + zend_hash_move_forward(Z_ARRVAL_PP(subject)); } } else { /* if subject is not an array */ @@ -1421,7 +1421,7 @@ static void preg_replace_impl(INTERNAL_FUNCTION_PARAMETERS, int is_callable_repl zval_dtor(*zcount); ZVAL_LONG(*zcount, replace_count); } - + } /* }}} */ @@ -1449,7 +1449,7 @@ static PHP_FUNCTION(preg_filter) } /* }}} */ -/* {{{ proto array preg_split(string pattern, string subject [, int limit [, int flags]]) +/* {{{ proto array preg_split(string pattern, string subject [, int limit [, int flags]]) Split string into an array using a perl-style regular expression as a delimiter */ static PHP_FUNCTION(preg_split) { @@ -1461,12 +1461,12 @@ static PHP_FUNCTION(preg_split) long flags = 0; /* Match control flags */ pcre_cache_entry *pce; /* Compiled regular expression */ - /* Get function parameters and do error checking */ + /* Get function parameters and do error checking */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|ll", ®ex, ®ex_len, &subject, &subject_len, &limit_val, &flags) == FAILURE) { RETURN_FALSE; } - + /* Compile regex or get it from cache. */ if ((pce = pcre_get_compiled_regex_cache(regex, regex_len TSRMLS_CC)) == NULL) { RETURN_FALSE; @@ -1501,7 +1501,7 @@ PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, char *subject, int subjec no_empty = flags & PREG_SPLIT_NO_EMPTY; delim_capture = flags & PREG_SPLIT_DELIM_CAPTURE; offset_capture = flags & PREG_SPLIT_OFFSET_CAPTURE; - + if (limit_val == 0) { limit_val = -1; } @@ -1512,7 +1512,7 @@ PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, char *subject, int subjec } extra->match_limit = PCRE_G(backtrack_limit); extra->match_limit_recursion = PCRE_G(recursion_limit); - + /* Initialize return value */ array_init(return_value); @@ -1524,13 +1524,13 @@ PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, char *subject, int subjec } size_offsets = (size_offsets + 1) * 3; offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0); - + /* Start at the beginning of the string */ start_offset = 0; next_offset = 0; last_match = subject; PCRE_G(error_code) = PHP_PCRE_NO_ERROR; - + /* Get next piece if no limit or limit not yet reached and something matched*/ while ((limit_val == -1 || limit_val > 1)) { count = pcre_exec(pce->re, extra, subject, @@ -1545,9 +1545,9 @@ PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, char *subject, int subjec php_error_docref(NULL TSRMLS_CC,E_NOTICE, "Matched, but too many substrings"); count = size_offsets/3; } - + /* If something matched */ - if (count > 0) { + if (count > 0 && (offsets[1] - offsets[0] >= 0)) { if (!no_empty || &subject[offsets[0]] != last_match) { if (offset_capture) { @@ -1563,7 +1563,7 @@ PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, char *subject, int subjec if (limit_val != -1) limit_val--; } - + last_match = &subject[offsets[1]]; next_offset = offsets[1]; @@ -1620,7 +1620,7 @@ PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, char *subject, int subjec the match again at the same point. If this fails (picked up above) we advance to the next character. */ g_notempty = (offsets[1] == offsets[0])? PCRE_NOTEMPTY | PCRE_ANCHORED : 0; - + /* Advance to the position right after the last full match */ start_offset = offsets[1]; } @@ -1639,7 +1639,7 @@ PHPAPI void php_pcre_split_impl(pcre_cache_entry *pce, char *subject, int subjec } } - + /* Clean up */ efree(offsets); } @@ -1660,13 +1660,13 @@ static PHP_FUNCTION(preg_quote) delim_char=0, /* Delimiter character to be quoted */ c; /* Current character */ zend_bool quote_delim = 0; /* Whether to quote additional delim char */ - + /* Get the arguments and check for errors */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &in_str, &in_str_len, &delim, &delim_len) == FAILURE) { return; } - + in_str_end = in_str + in_str_len; /* Nothing to do if we got an empty string */ @@ -1678,11 +1678,11 @@ static PHP_FUNCTION(preg_quote) delim_char = delim[0]; quote_delim = 1; } - + /* Allocate enough memory so that even if each character is quoted, we won't run out of room */ out_str = safe_emalloc(4, in_str_len, 1); - + /* Go through the string and quote necessary characters */ for(p = in_str, q = out_str; p != in_str_end; p++) { c = *p; @@ -1726,7 +1726,7 @@ static PHP_FUNCTION(preg_quote) } } *q = '\0'; - + /* Reallocate string and return it */ RETVAL_STRINGL(erealloc(out_str, q - out_str + 1), q - out_str, 0); } @@ -1747,12 +1747,12 @@ static PHP_FUNCTION(preg_grep) &input, &flags) == FAILURE) { return; } - + /* Compile regex or get it from cache. */ if ((pce = pcre_get_compiled_regex_cache(regex, regex_len TSRMLS_CC)) == NULL) { RETURN_FALSE; } - + php_pcre_grep_impl(pce, input, return_value, flags TSRMLS_CC); } /* }}} */ @@ -1770,9 +1770,9 @@ PHPAPI void php_pcre_grep_impl(pcre_cache_entry *pce, zval *input, zval *return zend_bool invert; /* Whether to return non-matching entries */ int rc; - + invert = flags & PREG_GREP_INVERT ? 1 : 0; - + if (extra == NULL) { extra_data.flags = PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION; extra = &extra_data; @@ -1788,7 +1788,7 @@ PHPAPI void php_pcre_grep_impl(pcre_cache_entry *pce, zval *input, zval *return } size_offsets = (size_offsets + 1) * 3; offsets = (int *)safe_emalloc(size_offsets, sizeof(int), 0); - + /* Initialize return array */ array_init(return_value); @@ -1901,7 +1901,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_split, 0, 0, 2) ZEND_ARG_INFO(0, pattern) ZEND_ARG_INFO(0, subject) ZEND_ARG_INFO(0, limit) - ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_preg_quote, 0, 0, 1) diff --git a/ext/pcre/tests/bug70345.phpt b/ext/pcre/tests/bug70345.phpt new file mode 100644 index 0000000000..0947ba3daa --- /dev/null +++ b/ext/pcre/tests/bug70345.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #70345 (Multiple vulnerabilities related to PCRE functions) +--FILE-- + aaaaxyzaaaa +) + +Warning: preg_match(): Get subpatterns list failed in %s on line %d +array(0) { +} -- cgit v1.2.1 From ce2c67c8e88ede70a3fe837a484fddc77aba4bb2 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Sat, 29 Aug 2015 23:01:36 -0700 Subject: Improve fix for #70385 --- ext/exif/exif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/exif/exif.c b/ext/exif/exif.c index 43e68c449e..05041c5f00 100644 --- a/ext/exif/exif.c +++ b/ext/exif/exif.c @@ -2964,13 +2964,13 @@ static int exif_process_IFD_TAG(image_info_type *ImageInfo, char *dir_entry, cha if (lengthCopyrightPhotographer = estrdup(value_ptr); - ImageInfo->CopyrightEditor = estrdup(value_ptr+length+1); + ImageInfo->CopyrightEditor = estrndup(value_ptr+length+1, byte_count-length-1); spprintf(&ImageInfo->Copyright, 0, "%s, %s", value_ptr, value_ptr+length+1); /* format = TAG_FMT_UNDEFINED; this musn't be ASCII */ /* but we are not supposed to change this */ /* keep in mind that image_info does not store editor value */ } else { - ImageInfo->Copyright = estrdup(value_ptr); + ImageInfo->Copyright = estrndup(value_ptr, byte_count); } } break; -- cgit v1.2.1 From f9c2bf73adb2ede0a486b0db466c264f2b27e0bb Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Sun, 30 Aug 2015 00:38:08 -0700 Subject: Fixed bug #70350: ZipArchive::extractTo allows for directory traversal when creating directories --- ext/zip/php_zip.c | 78 ++++++++++++++++++++++----------------------- ext/zip/tests/bug70350.phpt | 33 +++++++++++++++++++ 2 files changed, 72 insertions(+), 39 deletions(-) create mode 100644 ext/zip/tests/bug70350.phpt diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index abc21c7f15..6c872ebfb0 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -133,7 +133,7 @@ static char * php_zip_make_relative_path(char *path, int path_len) /* {{{ */ } /* }}} */ -#ifdef PHP_ZIP_USE_OO +#ifdef PHP_ZIP_USE_OO /* {{{ php_zip_extract_file */ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int file_len TSRMLS_DC) { @@ -174,7 +174,7 @@ static int php_zip_extract_file(struct zip * za, char *dest, char *file, int fil /* it is a directory only, see #40228 */ if (path_cleaned_len > 1 && IS_SLASH(path_cleaned[path_cleaned_len - 1])) { - len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, file); + len = spprintf(&file_dirname_fullpath, 0, "%s/%s", dest, path_cleaned); is_dir_only = 1; } else { memcpy(file_dirname, path_cleaned, path_cleaned_len); @@ -295,7 +295,7 @@ done: } /* }}} */ -static int php_zip_add_file(struct zip *za, const char *filename, size_t filename_len, +static int php_zip_add_file(struct zip *za, const char *filename, size_t filename_len, char *entry_name, size_t entry_name_len, long offset_start, long offset_len TSRMLS_DC) /* {{{ */ { struct zip_source *zs; @@ -345,7 +345,7 @@ static int php_zip_add_file(struct zip *za, const char *filename, size_t filenam } /* }}} */ -static int php_zip_parse_options(zval *options, long *remove_all_path, +static int php_zip_parse_options(zval *options, long *remove_all_path, char **remove_path, int *remove_path_len, char **add_path, int *add_path_len TSRMLS_DC) /* {{{ */ { zval **option; @@ -375,11 +375,11 @@ static int php_zip_parse_options(zval *options, long *remove_all_path, } if (Z_STRLEN_PP(option) >= MAXPATHLEN) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "remove_path string is too long (max: %i, %i given)", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "remove_path string is too long (max: %i, %i given)", MAXPATHLEN - 1, Z_STRLEN_PP(option)); return -1; } - *remove_path_len = Z_STRLEN_PP(option); + *remove_path_len = Z_STRLEN_PP(option); *remove_path = Z_STRVAL_PP(option); } @@ -395,11 +395,11 @@ static int php_zip_parse_options(zval *options, long *remove_all_path, } if (Z_STRLEN_PP(option) >= MAXPATHLEN) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "add_path string too long (max: %i, %i given)", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "add_path string too long (max: %i, %i given)", MAXPATHLEN - 1, Z_STRLEN_PP(option)); return -1; } - *add_path_len = Z_STRLEN_PP(option); + *add_path_len = Z_STRLEN_PP(option); *add_path = Z_STRVAL_PP(option); } return 1; @@ -528,7 +528,7 @@ int php_zip_glob(char *pattern, int pattern_len, long flags, zval *return_value glob_t globbuf; int n; int ret; - + if (pattern_len >= MAXPATHLEN) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Pattern exceeds the maximum allowed length of %d characters", MAXPATHLEN); return -1; @@ -539,9 +539,9 @@ int php_zip_glob(char *pattern, int pattern_len, long flags, zval *return_value return -1; } -#ifdef ZTS +#ifdef ZTS if (!IS_ABSOLUTE_PATH(pattern, pattern_len)) { - result = VCWD_GETCWD(cwd, MAXPATHLEN); + result = VCWD_GETCWD(cwd, MAXPATHLEN); if (!result) { cwd[0] = '\0'; } @@ -554,7 +554,7 @@ int php_zip_glob(char *pattern, int pattern_len, long flags, zval *return_value snprintf(work_pattern, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, pattern); pattern = work_pattern; - } + } #endif globbuf.gl_offs = 0; @@ -564,7 +564,7 @@ int php_zip_glob(char *pattern, int pattern_len, long flags, zval *return_value /* Some glob implementation simply return no data if no matches were found, others return the GLOB_NOMATCH error code. We don't want to treat GLOB_NOMATCH as an error condition - so that PHP glob() behaves the same on both types of + so that PHP glob() behaves the same on both types of implementations and so that 'foreach (glob() as ...' can be used for simple glob() calls without further error checking. @@ -593,11 +593,11 @@ int php_zip_glob(char *pattern, int pattern_len, long flags, zval *return_value for (n = 0; n < globbuf.gl_pathc; n++) { /* we need to do this everytime since GLOB_ONLYDIR does not guarantee that * all directories will be filtered. GNU libc documentation states the - * following: - * If the information about the type of the file is easily available - * non-directories will be rejected but no extra work will be done to - * determine the information for each file. I.e., the caller must still be - * able to filter directories out. + * following: + * If the information about the type of the file is easily available + * non-directories will be rejected but no extra work will be done to + * determine the information for each file. I.e., the caller must still be + * able to filter directories out. */ if (flags & GLOB_ONLYDIR) { struct stat s; @@ -633,9 +633,9 @@ int php_zip_pcre(char *regexp, int regexp_len, char *path, int path_len, zval *r int files_cnt; char **namelist; -#ifdef ZTS +#ifdef ZTS if (!IS_ABSOLUTE_PATH(path, path_len)) { - result = VCWD_GETCWD(cwd, MAXPATHLEN); + result = VCWD_GETCWD(cwd, MAXPATHLEN); if (!result) { cwd[0] = '\0'; } @@ -648,7 +648,7 @@ int php_zip_pcre(char *regexp, int regexp_len, char *path, int path_len, zval *r snprintf(work_path, MAXPATHLEN, "%s%c%s", cwd, DEFAULT_SLASH, path); path = work_path; - } + } #endif if (ZIP_OPENBASEDIR_CHECKPATH(path)) { @@ -665,7 +665,7 @@ int php_zip_pcre(char *regexp, int regexp_len, char *path, int path_len, zval *r re = pcre_get_compiled_regex(regexp, &pcre_extra, &preg_options TSRMLS_CC); if (!re) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid expression"); - return -1; + return -1; } array_init(return_value); @@ -678,7 +678,7 @@ int php_zip_pcre(char *regexp, int regexp_len, char *path, int path_len, zval *r int matches; int namelist_len = strlen(namelist[i]); - + if ((namelist_len == 1 && namelist[i][0] == '.') || (namelist_len == 2 && namelist[i][0] == '.' && namelist[i][1] == '.')) { efree(namelist[i]); @@ -686,7 +686,7 @@ int php_zip_pcre(char *regexp, int regexp_len, char *path, int path_len, zval *r } if ((path_len + namelist_len + 1) >= MAXPATHLEN) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "add_path string too long (max: %i, %i given)", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "add_path string too long (max: %i, %i given)", MAXPATHLEN - 1, (path_len + namelist_len + 1)); efree(namelist[i]); break; @@ -709,7 +709,7 @@ int php_zip_pcre(char *regexp, int regexp_len, char *path, int path_len, zval *r /* 0 means that the vector is too small to hold all the captured substring offsets */ if (matches < 0) { efree(namelist[i]); - continue; + continue; } add_next_index_string(return_value, fullpath, 1); @@ -785,7 +785,7 @@ static const zend_function_entry zip_functions[] = { /* }}} */ /* {{{ ZE2 OO definitions */ -#ifdef PHP_ZIP_USE_OO +#ifdef PHP_ZIP_USE_OO static zend_class_entry *zip_class_entry; static zend_object_handlers zip_object_handlers; @@ -805,7 +805,7 @@ typedef struct _zip_prop_handler { #endif /* }}} */ -#ifdef PHP_ZIP_USE_OO +#ifdef PHP_ZIP_USE_OO static void php_zip_register_prop_handler(HashTable *prop_handler, char *name, zip_read_int_t read_int_func, zip_read_const_char_t read_char_func, zip_read_const_char_from_ze_t read_char_from_obj_func, int rettype TSRMLS_DC) /* {{{ */ { zip_prop_handler hnd; @@ -893,7 +893,7 @@ static zval **php_zip_get_property_ptr_ptr(zval *object, zval *member, const zen if (obj->prop_handler != NULL) { if (key) { ret = zend_hash_quick_find(obj->prop_handler, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, key->hash_value, (void **) &hnd); - } else { + } else { ret = zend_hash_find(obj->prop_handler, Z_STRVAL_P(member), Z_STRLEN_P(member)+1, (void **) &hnd); } } @@ -1483,7 +1483,7 @@ static PHP_NAMED_FUNCTION(zif_zip_entry_compressionmethod) } /* }}} */ -#ifdef PHP_ZIP_USE_OO +#ifdef PHP_ZIP_USE_OO /* {{{ proto mixed ZipArchive::open(string source [, int flags]) Create new zip using source uri for output, return TRUE on success or the error code */ static ZIPARCHIVE_METHOD(open) @@ -1590,7 +1590,7 @@ static ZIPARCHIVE_METHOD(getStatusString) zip_error_get(intern, &zep, &syp); len = zip_error_to_str(error_string, 128, zep, syp); - RETVAL_STRINGL(error_string, len, 1); + RETVAL_STRINGL(error_string, len, 1); } /* }}} */ @@ -1667,12 +1667,12 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* ZIP_FROM_OBJECT(intern, this); /* 1 == glob, 2==pcre */ if (type == 1) { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|la", + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|la", &pattern, &pattern_len, &flags, &options) == FAILURE) { return; } } else { - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|sa", + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|sa", &pattern, &pattern_len, &path, &path_len, &options) == FAILURE) { return; } @@ -1724,14 +1724,14 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* if (add_path) { if ((add_path_len + file_stripped_len) > MAXPATHLEN) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Entry name too long (max: %d, %ld given)", + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Entry name too long (max: %d, %ld given)", MAXPATHLEN - 1, (add_path_len + file_stripped_len)); zval_dtor(return_value); RETURN_FALSE; } snprintf(entry_name_buf, MAXPATHLEN, "%s%s", add_path, file_stripped); - entry_name = entry_name_buf; + entry_name = entry_name_buf; entry_name_len = strlen(entry_name); } else { entry_name = Z_STRVAL_PP(zval_file); @@ -1741,7 +1741,7 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* efree(basename); basename = NULL; } - if (php_zip_add_file(intern, Z_STRVAL_PP(zval_file), Z_STRLEN_PP(zval_file), + if (php_zip_add_file(intern, Z_STRVAL_PP(zval_file), Z_STRLEN_PP(zval_file), entry_name, entry_name_len, 0, 0 TSRMLS_CC) < 0) { zval_dtor(return_value); RETURN_FALSE; @@ -1801,7 +1801,7 @@ static ZIPARCHIVE_METHOD(addFile) entry_name_len = filename_len; } - if (php_zip_add_file(intern, filename, filename_len, + if (php_zip_add_file(intern, filename, filename_len, entry_name, entry_name_len, 0, 0 TSRMLS_CC) < 0) { RETURN_FALSE; } else { @@ -1865,7 +1865,7 @@ static ZIPARCHIVE_METHOD(addFromString) } fail: zip_source_free(zs); - RETURN_FALSE; + RETURN_FALSE; } /* }}} */ @@ -2770,7 +2770,7 @@ static const zend_function_entry zip_class_functions[] = { /* {{{ PHP_MINIT_FUNCTION */ static PHP_MINIT_FUNCTION(zip) { -#ifdef PHP_ZIP_USE_OO +#ifdef PHP_ZIP_USE_OO zend_class_entry ce; memcpy(&zip_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); @@ -2859,7 +2859,7 @@ static PHP_MINIT_FUNCTION(zip) */ static PHP_MSHUTDOWN_FUNCTION(zip) { -#ifdef PHP_ZIP_USE_OO +#ifdef PHP_ZIP_USE_OO zend_hash_destroy(&zip_prop_handlers); php_unregister_url_stream_wrapper("zip" TSRMLS_CC); #endif diff --git a/ext/zip/tests/bug70350.phpt b/ext/zip/tests/bug70350.phpt new file mode 100644 index 0000000000..c308f549cf --- /dev/null +++ b/ext/zip/tests/bug70350.phpt @@ -0,0 +1,33 @@ +--TEST-- +Bug #70350 (ZipArchive::extractTo allows for directory traversal when creating directories) +--SKIPIF-- + +--FILE-- +open("$dir/a.zip",ZipArchive::CREATE); +$archive->addEmptyDir("../down2/"); +$archive->close(); + +$archive2 = new ZipArchive(); +$archive2->open('a.zip'); +$archive2->extractTo($dir); +$archive2->close(); +var_dump(file_exists("$dir/down2/")); +var_dump(file_exists("../down2/")); +?> +--CLEAN-- + +--EXPECT-- +bool(true) +bool(false) -- cgit v1.2.1 From e201f01ac17243a1e5fb6a3911ed8e21b1619ac1 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Mon, 31 Aug 2015 21:06:03 -0700 Subject: Fix bug #70388 - SOAP serialize_function_call() type confusion --- ext/soap/soap.c | 96 ++++++++++++++++++++++++-------------------- ext/soap/tests/bug70388.phpt | 17 ++++++++ 2 files changed, 69 insertions(+), 44 deletions(-) create mode 100644 ext/soap/tests/bug70388.phpt diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 1b8f545b85..a0e64a3900 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -989,7 +989,7 @@ static HashTable* soap_create_typemap(sdlPtr sdl, HashTable *ht TSRMLS_DC) HashTable *ht2; HashPosition pos1, pos2; HashTable *typemap = NULL; - + zend_hash_internal_pointer_reset_ex(ht, &pos1); while (zend_hash_get_current_data_ex(ht, (void**)&tmp, &pos1) == SUCCESS) { char *type_name = NULL; @@ -1033,7 +1033,7 @@ static HashTable* soap_create_typemap(sdlPtr sdl, HashTable *ht TSRMLS_DC) } } zend_hash_move_forward_ex(ht2, &pos2); - } + } if (type_name) { smart_str nscat = {0}; @@ -1063,7 +1063,7 @@ static HashTable* soap_create_typemap(sdlPtr sdl, HashTable *ht TSRMLS_DC) new_enc->to_xml = enc->to_xml; new_enc->to_zval = enc->to_zval; new_enc->details.map = emalloc(sizeof(soapMapping)); - memset(new_enc->details.map, 0, sizeof(soapMapping)); + memset(new_enc->details.map, 0, sizeof(soapMapping)); if (to_xml) { zval_add_ref(&to_xml); new_enc->details.map->to_xml = to_xml; @@ -1120,7 +1120,7 @@ PHP_METHOD(SoapServer, SoapServer) if (Z_TYPE_P(wsdl) != IS_STRING && Z_TYPE_P(wsdl) != IS_NULL) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid parameters"); } - + service = emalloc(sizeof(soapService)); memset(service, 0, sizeof(soapService)); service->send_errors = 1; @@ -1155,7 +1155,7 @@ PHP_METHOD(SoapServer, SoapServer) if (zend_hash_find(ht, "encoding", sizeof("encoding"), (void**)&tmp) == SUCCESS && Z_TYPE_PP(tmp) == IS_STRING) { xmlCharEncodingHandlerPtr encoding; - + encoding = xmlFindCharEncodingHandler(Z_STRVAL_PP(tmp)); if (encoding == NULL) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid 'encoding' option - '%s'", Z_STRVAL_PP(tmp)); @@ -1215,7 +1215,7 @@ PHP_METHOD(SoapServer, SoapServer) } } } - + if (typemap_ht) { service->typemap = soap_create_typemap(service->sdl, typemap_ht TSRMLS_CC); } @@ -1345,7 +1345,7 @@ PHP_METHOD(SoapServer, getFunctions) if (zend_parse_parameters_none() == FAILURE) { return; } - + FETCH_THIS_SERVICE(service); array_init(return_value); @@ -1505,7 +1505,7 @@ PHP_METHOD(SoapServer, handle) FETCH_THIS_SERVICE(service); SOAP_GLOBAL(soap_version) = service->version; - + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &arg, &arg_len) == FAILURE) { return; } @@ -1930,7 +1930,7 @@ PHP_METHOD(SoapServer, handle) if (size == 0) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Dump memory failed"); - } + } if (soap_version == SOAP_1_2) { sapi_add_header("Content-Type: application/soap+xml; charset=utf-8", sizeof("Content-Type: application/soap+xml; charset=utf-8")-1, 1); @@ -2147,10 +2147,10 @@ static void soap_error_handler(int error_num, const char *error_filename, const use_exceptions = 1; } - if ((error_num == E_USER_ERROR || - error_num == E_COMPILE_ERROR || + if ((error_num == E_USER_ERROR || + error_num == E_COMPILE_ERROR || error_num == E_CORE_ERROR || - error_num == E_ERROR || + error_num == E_ERROR || error_num == E_PARSE) && use_exceptions) { zval *fault, *exception; @@ -2219,10 +2219,10 @@ static void soap_error_handler(int error_num, const char *error_filename, const va_list argcopy; #endif - if (error_num == E_USER_ERROR || - error_num == E_COMPILE_ERROR || + if (error_num == E_USER_ERROR || + error_num == E_COMPILE_ERROR || error_num == E_CORE_ERROR || - error_num == E_ERROR || + error_num == E_ERROR || error_num == E_PARSE) { char* code = SOAP_GLOBAL(error_code); @@ -2452,13 +2452,13 @@ PHP_METHOD(SoapClient, SoapClient) if (zend_hash_find(ht, "encoding", sizeof("encoding"), (void**)&tmp) == SUCCESS && Z_TYPE_PP(tmp) == IS_STRING) { xmlCharEncodingHandlerPtr encoding; - + encoding = xmlFindCharEncodingHandler(Z_STRVAL_PP(tmp)); if (encoding == NULL) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Invalid 'encoding' option - '%s'", Z_STRVAL_PP(tmp)); } else { xmlCharEncCloseFunc(encoding); - add_property_stringl(this_ptr, "_encoding", Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp), 1); + add_property_stringl(this_ptr, "_encoding", Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp), 1); } } if (zend_hash_find(ht, "classmap", sizeof("classmap"), (void**)&tmp) == SUCCESS && @@ -2493,7 +2493,7 @@ PHP_METHOD(SoapClient, SoapClient) if (context) { add_property_resource(this_ptr, "_stream_context", context->rsrc_id); } - + if (zend_hash_find(ht, "cache_wsdl", sizeof("cache_wsdl"), (void**)&tmp) == SUCCESS && Z_TYPE_PP(tmp) == IS_LONG) { cache_wsdl = Z_LVAL_PP(tmp); @@ -2503,7 +2503,7 @@ PHP_METHOD(SoapClient, SoapClient) Z_TYPE_PP(tmp) == IS_STRING) { add_property_stringl(this_ptr, "_user_agent", Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp), 1); } - + if (zend_hash_find(ht, "keep_alive", sizeof("keep_alive"), (void**)&tmp) == SUCCESS && (Z_TYPE_PP(tmp) == IS_BOOL || Z_TYPE_PP(tmp) == IS_LONG) && Z_LVAL_PP(tmp) == 0) { add_property_long(this_ptr, "_keep_alive", 0); @@ -2606,7 +2606,7 @@ static int do_request(zval *this_ptr, xmlDoc *request, char *location, char *act xmlFree(buf); if (ret && zend_hash_find(Z_OBJPROP_P(this_ptr), "__soap_fault", sizeof("__soap_fault"), (void **) &fault) == SUCCESS) { return FALSE; - } + } return ret; } @@ -2903,8 +2903,10 @@ PHP_METHOD(SoapClient, __call) } zend_hash_internal_pointer_reset(default_headers); while (zend_hash_get_current_data(default_headers, (void**)&tmp) == SUCCESS) { - Z_ADDREF_PP(tmp); - zend_hash_next_index_insert(soap_headers, tmp, sizeof(zval *), NULL); + if(Z_TYPE_PP(tmp) == IS_OBJECT) { + Z_ADDREF_PP(tmp); + zend_hash_next_index_insert(soap_headers, tmp, sizeof(zval *), NULL); + } zend_hash_move_forward(default_headers); } } else { @@ -2912,7 +2914,7 @@ PHP_METHOD(SoapClient, __call) free_soap_headers = 0; } } - + arg_count = zend_hash_num_elements(Z_ARRVAL_P(args)); if (arg_count > 0) { @@ -2978,7 +2980,7 @@ PHP_METHOD(SoapClient, __getTypes) HashPosition pos; FETCH_THIS_SDL(sdl); - + if (zend_parse_parameters_none() == FAILURE) { return; } @@ -3007,7 +3009,7 @@ PHP_METHOD(SoapClient, __getTypes) PHP_METHOD(SoapClient, __getLastRequest) { zval **tmp; - + if (zend_parse_parameters_none() == FAILURE) { return; } @@ -3030,7 +3032,7 @@ PHP_METHOD(SoapClient, __getLastResponse) if (zend_parse_parameters_none() == FAILURE) { return; } - + if (zend_hash_find(Z_OBJPROP_P(this_ptr), "__last_response", sizeof("__last_response"), (void **)&tmp) == SUCCESS && Z_TYPE_PP(tmp) == IS_STRING) { RETURN_STRINGL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp), 1); @@ -3045,11 +3047,11 @@ PHP_METHOD(SoapClient, __getLastResponse) PHP_METHOD(SoapClient, __getLastRequestHeaders) { zval **tmp; - + if (zend_parse_parameters_none() == FAILURE) { return; } - + if (zend_hash_find(Z_OBJPROP_P(this_ptr), "__last_request_headers", sizeof("__last_request_headers"), (void **)&tmp) == SUCCESS && Z_TYPE_PP(tmp) == IS_STRING) { RETURN_STRINGL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp), 1); @@ -3064,7 +3066,7 @@ PHP_METHOD(SoapClient, __getLastRequestHeaders) PHP_METHOD(SoapClient, __getLastResponseHeaders) { zval **tmp; - + if (zend_parse_parameters_none() == FAILURE) { return; } @@ -3209,10 +3211,10 @@ PHP_METHOD(SoapClient, __setSoapHeaders) /* {{{ proto string SoapClient::__setLocation([string new_location]) - Sets the location option (the endpoint URL that will be touched by the + Sets the location option (the endpoint URL that will be touched by the following SOAP requests). If new_location is not specified or null then SoapClient will use endpoint - from WSDL file. + from WSDL file. The function returns old value of location options. */ PHP_METHOD(SoapClient, __setLocation) { @@ -3261,10 +3263,10 @@ static void set_soap_fault(zval *obj, char *fault_code_ns, char *fault_code, cha if (Z_TYPE_P(obj) != IS_OBJECT) { object_init_ex(obj, soap_fault_class_entry); } - + add_property_string(obj, "faultstring", fault_string ? fault_string : "", 1); zend_update_property_string(zend_exception_get_default(TSRMLS_C), obj, "message", sizeof("message")-1, (fault_string ? fault_string : "") TSRMLS_CC); - + if (fault_code != NULL) { int soap_version = SOAP_GLOBAL(soap_version); @@ -3689,7 +3691,7 @@ ignore_header: func = func->children; } deserialize_parameters(func, function, num_params, parameters TSRMLS_CC); - + encode_finish(); return function; @@ -3971,8 +3973,8 @@ static xmlDocPtr serialize_response_call(sdlFunctionPtr function, char *function } if (fault_ns == NULL && - fault && - fault->details && + fault && + fault->details && zend_hash_num_elements(fault->details) == 1) { sdlParamPtr sparam; @@ -3996,7 +3998,7 @@ static xmlDocPtr serialize_response_call(sdlFunctionPtr function, char *function xmlChar *code = xmlBuildQName(BAD_CAST(str), nsptr->prefix, NULL, 0); xmlNodeSetContent(node, code); xmlFree(code); - } else { + } else { xmlNodeSetContentLen(node, BAD_CAST(str), (int)new_len); } efree(str); @@ -4022,7 +4024,7 @@ static xmlDocPtr serialize_response_call(sdlFunctionPtr function, char *function xmlChar *code = xmlBuildQName(BAD_CAST(str), nsptr->prefix, NULL, 0); xmlNodeSetContent(node, code); xmlFree(code); - } else { + } else { xmlNodeSetContentLen(node, BAD_CAST(str), (int)new_len); } efree(str); @@ -4194,7 +4196,7 @@ static xmlDocPtr serialize_response_call(sdlFunctionPtr function, char *function encode_finish(); - if (function && function->responseName == NULL && + if (function && function->responseName == NULL && body->children == NULL && head == NULL) { xmlFreeDoc(doc); return NULL; @@ -4328,11 +4330,18 @@ static xmlDocPtr serialize_function_call(zval *this_ptr, sdlFunctionPtr function if (head) { zval** header; - zend_hash_internal_pointer_reset(soap_headers); - while (zend_hash_get_current_data(soap_headers,(void**)&header) == SUCCESS) { - HashTable *ht = Z_OBJPROP_PP(header); + for(zend_hash_internal_pointer_reset(soap_headers); + zend_hash_get_current_data(soap_headers,(void**)&header) == SUCCESS; + zend_hash_move_forward(soap_headers) + ) { + HashTable *ht; zval **name, **ns, **tmp; + if (Z_TYPE_PP(header) != IS_OBJECT) { + continue; + } + + ht = Z_OBJPROP_PP(header); if (zend_hash_find(ht, "name", sizeof("name"), (void**)&name) == SUCCESS && Z_TYPE_PP(name) == IS_STRING && zend_hash_find(ht, "namespace", sizeof("namespace"), (void**)&ns) == SUCCESS && @@ -4371,7 +4380,6 @@ static xmlDocPtr serialize_function_call(zval *this_ptr, sdlFunctionPtr function xmlSetNs(h, nsptr); set_soap_header_attributes(h, ht, version); } - zend_hash_move_forward(soap_headers); } } @@ -4482,7 +4490,7 @@ static sdlParamPtr get_param(sdlFunctionPtr function, char *param_name, int inde return *tmp; } else { HashPosition pos; - + zend_hash_internal_pointer_reset_ex(ht, &pos); while (zend_hash_get_current_data_ex(ht, (void **)&tmp, &pos) != FAILURE) { if ((*tmp)->paramName && strcmp(param_name, (*tmp)->paramName) == 0) { diff --git a/ext/soap/tests/bug70388.phpt b/ext/soap/tests/bug70388.phpt new file mode 100644 index 0000000000..49a8efc0ff --- /dev/null +++ b/ext/soap/tests/bug70388.phpt @@ -0,0 +1,17 @@ +--TEST-- +Bug #70388 (SOAP serialize_function_call() type confusion / RCE) +--SKIPIF-- + +--FILE-- +notexisting()); +} catch(Exception $e) { + var_dump($e->getMessage()); + var_dump(get_class($e)); +} +?> +--EXPECTF-- +string(%d) "%s" +string(9) "SoapFault" \ No newline at end of file -- cgit v1.2.1 From e8429400d40e3c3aa4b22ba701991d698a2f3b2f Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Mon, 31 Aug 2015 21:28:11 -0700 Subject: Fix bug #70172 - Use After Free Vulnerability in unserialize() --- ext/standard/tests/serialize/bug70172.phpt | 52 ++++++++++++++++++++ ext/standard/var.c | 23 +++++++-- ext/standard/var_unserializer.c | 76 ++++++++++++++++-------------- ext/standard/var_unserializer.re | 12 +++-- 4 files changed, 121 insertions(+), 42 deletions(-) create mode 100644 ext/standard/tests/serialize/bug70172.phpt diff --git a/ext/standard/tests/serialize/bug70172.phpt b/ext/standard/tests/serialize/bug70172.phpt new file mode 100644 index 0000000000..0e9d7ed89a --- /dev/null +++ b/ext/standard/tests/serialize/bug70172.phpt @@ -0,0 +1,52 @@ +--TEST-- +Bug #70172 - Use After Free Vulnerability in unserialize() +--FILE-- +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 = 'r:2;'; +$exploit = 'a:2:{i:0;i:1;i:1;C:3:"obj":'.strlen($inner).':{'.$inner.'}}'; + +$data = unserialize($exploit); + +for ($i = 0; $i < 5; $i++) { + $v[$i] = $fakezval.$i; +} + +var_dump($data); + +function ptr2str($ptr) +{ + $out = ''; + for ($i = 0; $i < 8; $i++) { + $out .= chr($ptr & 0xff); + $ptr >>= 8; + } + return $out; +} +?> +--EXPECTF-- +array(2) { + [0]=> + int(1) + [1]=> + object(obj)#%d (1) { + ["data"]=> + int(1) + } +} \ No newline at end of file diff --git a/ext/standard/var.c b/ext/standard/var.c index 7603ff2ee0..33b976f42d 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -373,7 +373,7 @@ static int php_array_element_export(zval **zv TSRMLS_DC, int num_args, va_list a smart_str_appendc(buf, ','); smart_str_appendc(buf, '\n'); - + return 0; } /* }}} */ @@ -392,7 +392,7 @@ static int php_object_element_export(zval **zv TSRMLS_DC, int num_args, va_list const char *pname; char *pname_esc; int pname_esc_len; - + zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1, &class_name, &pname); pname_esc = php_addcslashes(pname, strlen(pname), &pname_esc_len, 0, @@ -469,7 +469,7 @@ PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC) buffer_append_spaces(buf, level - 1); } smart_str_appendc(buf, ')'); - + break; case IS_OBJECT: @@ -802,7 +802,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var BG(serialize_lock)++; res = call_user_function_ex(CG(function_table), &struc, &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC); BG(serialize_lock)--; - + if (EG(exception)) { if (retval_ptr) { zval_ptr_dtor(&retval_ptr); @@ -951,6 +951,8 @@ PHP_FUNCTION(unserialize) int buf_len; const unsigned char *p; php_unserialize_data_t var_hash; + int oldlevel; + zval *old_rval = return_value; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) { RETURN_FALSE; @@ -970,6 +972,19 @@ PHP_FUNCTION(unserialize) } RETURN_FALSE; } + if (return_value != old_rval) { + /* + * Terrible hack due to the fact that executor passes us zval *, + * but unserialize with r/R wants to replace it with another zval * + */ + zval_dtor(old_rval); + *old_rval = *return_value; + zval_copy_ctor(old_rval); + var_push_dtor_no_addref(&var_hash, &return_value); + var_push_dtor_no_addref(&var_hash, &old_rval); + } else { + var_push_dtor(&var_hash, &return_value); + } PHP_VAR_UNSERIALIZE_DESTROY(var_hash); } /* }}} */ diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index ffaf680c51..5f2336ec14 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.7.5 on Sun Aug 23 19:50:03 2015 */ +/* Generated by re2c 0.13.7.5 on Mon Aug 31 23:15:46 2015 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -69,7 +69,7 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) var_hash = (*var_hashx)->last_dtor; #if VAR_ENTRIES_DBG - fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval)); + fprintf(stderr, "var_push_dtor(%p, %ld): %d\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval)); #endif if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) { @@ -100,7 +100,7 @@ PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rv var_hash = (*var_hashx)->last_dtor; #if VAR_ENTRIES_DBG - fprintf(stderr, "var_push_dtor_no_addref(%ld): %d (%d)\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval)); + fprintf(stderr, "var_push_dtor_no_addref(%p, %ld): %d (%d)\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval)); #endif if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) { @@ -179,6 +179,9 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) while (var_hash) { for (i = 0; i < var_hash->used_slots; i++) { +#if VAR_ENTRIES_DBG + fprintf(stderr, "var_destroy dtor(%p, %ld)\n", var_hash->data[i], Z_REFCOUNT_P(var_hash->data[i])); +#endif zval_ptr_dtor(&var_hash->data[i]); } next = var_hash->next; @@ -239,7 +242,7 @@ static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen #define YYMARKER marker -#line 247 "ext/standard/var_unserializer.re" +#line 250 "ext/standard/var_unserializer.re" @@ -484,7 +487,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) -#line 488 "ext/standard/var_unserializer.c" +#line 491 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -544,9 +547,9 @@ yy2: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 839 "ext/standard/var_unserializer.re" +#line 845 "ext/standard/var_unserializer.re" { return 0; } -#line 550 "ext/standard/var_unserializer.c" +#line 553 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -589,13 +592,13 @@ yy13: goto yy3; yy14: ++YYCURSOR; -#line 833 "ext/standard/var_unserializer.re" +#line 839 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } -#line 599 "ext/standard/var_unserializer.c" +#line 602 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -626,7 +629,7 @@ yy20: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 687 "ext/standard/var_unserializer.re" +#line 692 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; long elements; @@ -642,6 +645,7 @@ yy20: zval **args[1]; zval *arg_func_name; + if (!var_hash) return 0; if (*start == 'C') { custom_object = 1; } @@ -772,7 +776,7 @@ yy20: return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 776 "ext/standard/var_unserializer.c" +#line 780 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -797,15 +801,16 @@ yy27: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 679 "ext/standard/var_unserializer.re" +#line 683 "ext/standard/var_unserializer.re" { + if (!var_hash) return 0; INIT_PZVAL(*rval); return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 809 "ext/standard/var_unserializer.c" +#line 814 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -826,11 +831,12 @@ yy34: yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 659 "ext/standard/var_unserializer.re" +#line 662 "ext/standard/var_unserializer.re" { 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; @@ -846,7 +852,7 @@ yy34: return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 850 "ext/standard/var_unserializer.c" +#line 856 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -867,7 +873,7 @@ yy41: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 630 "ext/standard/var_unserializer.re" +#line 633 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -896,7 +902,7 @@ yy41: ZVAL_STRINGL(*rval, str, len, 0); return 1; } -#line 900 "ext/standard/var_unserializer.c" +#line 906 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -917,7 +923,7 @@ yy48: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 602 "ext/standard/var_unserializer.re" +#line 605 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -945,7 +951,7 @@ yy48: ZVAL_STRINGL(*rval, str, len, 1); return 1; } -#line 949 "ext/standard/var_unserializer.c" +#line 955 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -1033,7 +1039,7 @@ yy61: } yy63: ++YYCURSOR; -#line 592 "ext/standard/var_unserializer.re" +#line 595 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 use_double: @@ -1043,7 +1049,7 @@ use_double: ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); return 1; } -#line 1047 "ext/standard/var_unserializer.c" +#line 1053 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1102,7 +1108,7 @@ yy73: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 577 "ext/standard/var_unserializer.re" +#line 580 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); @@ -1117,7 +1123,7 @@ yy73: return 1; } -#line 1121 "ext/standard/var_unserializer.c" +#line 1127 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1144,7 +1150,7 @@ yy79: if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 550 "ext/standard/var_unserializer.re" +#line 553 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1171,7 +1177,7 @@ yy79: ZVAL_LONG(*rval, parse_iv(start + 2)); return 1; } -#line 1175 "ext/standard/var_unserializer.c" +#line 1181 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1179,24 +1185,24 @@ yy83: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 543 "ext/standard/var_unserializer.re" +#line 546 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); return 1; } -#line 1190 "ext/standard/var_unserializer.c" +#line 1196 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 536 "ext/standard/var_unserializer.re" +#line 539 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); return 1; } -#line 1200 "ext/standard/var_unserializer.c" +#line 1206 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1219,7 +1225,7 @@ yy91: if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 513 "ext/standard/var_unserializer.re" +#line 516 "ext/standard/var_unserializer.re" { long id; @@ -1242,7 +1248,7 @@ yy91: return 1; } -#line 1246 "ext/standard/var_unserializer.c" +#line 1252 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1265,7 +1271,7 @@ yy97: if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 492 "ext/standard/var_unserializer.re" +#line 495 "ext/standard/var_unserializer.re" { long id; @@ -1278,7 +1284,7 @@ yy97: } if (*rval != NULL) { - zval_ptr_dtor(rval); + var_push_dtor_no_addref(var_hash, rval); } *rval = *rval_ref; Z_ADDREF_PP(rval); @@ -1286,9 +1292,9 @@ yy97: return 1; } -#line 1290 "ext/standard/var_unserializer.c" +#line 1296 "ext/standard/var_unserializer.c" } -#line 841 "ext/standard/var_unserializer.re" +#line 847 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index f02602cd7e..ed821521e0 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -67,7 +67,7 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) var_hash = (*var_hashx)->last_dtor; #if VAR_ENTRIES_DBG - fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval)); + fprintf(stderr, "var_push_dtor(%p, %ld): %d\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval)); #endif if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) { @@ -98,7 +98,7 @@ PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rv var_hash = (*var_hashx)->last_dtor; #if VAR_ENTRIES_DBG - fprintf(stderr, "var_push_dtor_no_addref(%ld): %d (%d)\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval)); + fprintf(stderr, "var_push_dtor_no_addref(%p, %ld): %d (%d)\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval)); #endif if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) { @@ -177,6 +177,9 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) while (var_hash) { for (i = 0; i < var_hash->used_slots; i++) { +#if VAR_ENTRIES_DBG + fprintf(stderr, "var_destroy dtor(%p, %ld)\n", var_hash->data[i], Z_REFCOUNT_P(var_hash->data[i])); +#endif zval_ptr_dtor(&var_hash->data[i]); } next = var_hash->next; @@ -501,7 +504,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) } if (*rval != NULL) { - zval_ptr_dtor(rval); + var_push_dtor_no_addref(var_hash, rval); } *rval = *rval_ref; Z_ADDREF_PP(rval); @@ -660,6 +663,7 @@ use_double: 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; @@ -677,6 +681,7 @@ use_double: } "o:" iv ":" ["] { + if (!var_hash) return 0; INIT_PZVAL(*rval); @@ -699,6 +704,7 @@ object ":" uiv ":" ["] { zval **args[1]; zval *arg_func_name; + if (!var_hash) return 0; if (*start == 'C') { custom_object = 1; } -- cgit v1.2.1 From f06a069c462d37c2e009f6d1d93b8c8e7b713393 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 1 Sep 2015 00:14:15 -0700 Subject: Fix bug #70365 - use-after-free vulnerability in unserialize() with SplObjectStorage --- ext/spl/spl_observer.c | 2 ++ ext/spl/tests/bug70365.phpt | 50 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 ext/spl/tests/bug70365.phpt diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index 5d94a3b7b3..6a2e3211e5 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -853,6 +853,7 @@ SPL_METHOD(SplObjectStorage, unserialize) zval_ptr_dtor(&pentry); goto outexcept; } + var_push_dtor(&var_hash, &pentry); if(Z_TYPE_P(pentry) != IS_OBJECT) { zval_ptr_dtor(&pentry); goto outexcept; @@ -864,6 +865,7 @@ SPL_METHOD(SplObjectStorage, unserialize) zval_ptr_dtor(&pinf); goto outexcept; } + var_push_dtor(&var_hash, &pinf); } hash = spl_object_storage_get_hash(intern, getThis(), pentry, &hash_len TSRMLS_CC); diff --git a/ext/spl/tests/bug70365.phpt b/ext/spl/tests/bug70365.phpt new file mode 100644 index 0000000000..bd57360d3a --- /dev/null +++ b/ext/spl/tests/bug70365.phpt @@ -0,0 +1,50 @@ +--TEST-- +SPL: Bug #70365 yet another use-after-free vulnerability in unserialize() with SplObjectStorage +--FILE-- +ryat = 1; + } +} + +$fakezval = ptr2str(1122334455); +$fakezval .= ptr2str(0); +$fakezval .= "\x00\x00\x00\x00"; +$fakezval .= "\x01"; +$fakezval .= "\x00"; +$fakezval .= "\x00\x00"; + +$inner = 'x:i:1;O:8:"stdClass":0:{},i:1;;m:a:0:{}'; +$exploit = 'a:5:{i:0;i:1;i:1;C:16:"SplObjectStorage":'.strlen($inner).':{'.$inner.'}i:2;O:3:"obj":1:{s:4:"ryat";R:3;}i:3;R:6;i:4;s:'.strlen($fakezval).':"'.$fakezval.'";}'; + +$data = unserialize($exploit); + +var_dump($data); + +function ptr2str($ptr) +{ + $out = ''; + for ($i = 0; $i < 8; $i++) { + $out .= chr($ptr & 0xff); + $ptr >>= 8; + } + return $out; +} +--EXPECTF-- +array(5) { + [0]=> + int(1) + [1]=> + &int(1) + [2]=> + object(obj)#%d (1) { + ["ryat"]=> + &int(1) + } + [3]=> + int(1) + [4]=> + string(24) "%s" +} -- cgit v1.2.1 From 259057b2a484747a6c73ce54c4fa0f5acbd56179 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 1 Sep 2015 00:20:45 -0700 Subject: Fix bug #70366 - use-after-free vulnerability in unserialize() with SplDoublyLinkedList --- ext/spl/spl_dllist.c | 1 + ext/spl/tests/bug70365.phpt | 2 +- ext/spl/tests/bug70366.phpt | 54 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 ext/spl/tests/bug70366.phpt diff --git a/ext/spl/spl_dllist.c b/ext/spl/spl_dllist.c index 011d7a6e3c..ebe61c3f7a 100644 --- a/ext/spl/spl_dllist.c +++ b/ext/spl/spl_dllist.c @@ -1221,6 +1221,7 @@ SPL_METHOD(SplDoublyLinkedList, unserialize) zval_ptr_dtor(&elem); goto error; } + var_push_dtor(&var_hash, &elem); spl_ptr_llist_push(intern->llist, elem TSRMLS_CC); } diff --git a/ext/spl/tests/bug70365.phpt b/ext/spl/tests/bug70365.phpt index bd57360d3a..c18110e3ca 100644 --- a/ext/spl/tests/bug70365.phpt +++ b/ext/spl/tests/bug70365.phpt @@ -1,5 +1,5 @@ --TEST-- -SPL: Bug #70365 yet another use-after-free vulnerability in unserialize() with SplObjectStorage +SPL: Bug #70365 use-after-free vulnerability in unserialize() with SplObjectStorage --FILE-- ryat = 1; + } +} + +$fakezval = ptr2str(1122334455); +$fakezval .= ptr2str(0); +$fakezval .= "\x00\x00\x00\x00"; +$fakezval .= "\x01"; +$fakezval .= "\x00"; +$fakezval .= "\x00\x00"; + +$inner = 'i:1234;:i:1;'; +$exploit = 'a:5:{i:0;i:1;i:1;C:19:"SplDoublyLinkedList":'.strlen($inner).':{'.$inner.'}i:2;O:3:"obj":1:{s:4:"ryat";R:3;}i:3;a:1:{i:0;R:5;}i:4;s:'.strlen($fakezval).':"'.$fakezval.'";}'; + +$data = unserialize($exploit); + +var_dump($data); + +function ptr2str($ptr) +{ + $out = ''; + for ($i = 0; $i < 8; $i++) { + $out .= chr($ptr & 0xff); + $ptr >>= 8; + } + return $out; +} +?> +--EXPECTF-- +array(5) { + [0]=> + int(1) + [1]=> + &int(1) + [2]=> + object(obj)#%d (1) { + ["ryat"]=> + &int(1) + } + [3]=> + array(1) { + [0]=> + int(1) + } + [4]=> + string(24) "%s" +} \ No newline at end of file -- cgit v1.2.1 From c8f07ad4771620252bf542e09938633bfb837363 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 1 Sep 2015 00:26:12 -0700 Subject: add test --- ext/spl/tests/bug70155.phpt | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 ext/spl/tests/bug70155.phpt diff --git a/ext/spl/tests/bug70155.phpt b/ext/spl/tests/bug70155.phpt new file mode 100644 index 0000000000..1730a1a587 --- /dev/null +++ b/ext/spl/tests/bug70155.phpt @@ -0,0 +1,50 @@ +--TEST-- +SPL: Bug #70155 Use After Free Vulnerability in unserialize() with SPLArrayObject +--FILE-- + +===DONE=== +--EXPECTF-- +object(ArrayObject)#1 (2) { + [0]=> + int(0) + ["storage":"ArrayObject":private]=> + object(DateInterval)#2 (15) { + ["y"]=> + int(3) + ["m"]=> + int(-1) + ["d"]=> + int(-1) + ["h"]=> + int(-1) + ["i"]=> + int(-1) + ["s"]=> + int(-1) + ["weekday"]=> + int(-1) + ["weekday_behavior"]=> + int(-1) + ["first_last_day_of"]=> + int(-1) + ["invert"]=> + int(0) + ["days"]=> + int(-1) + ["special_type"]=> + int(0) + ["special_amount"]=> + int(-1) + ["have_weekday_relative"]=> + int(0) + ["have_special_relative"]=> + int(0) + } +} +===DONE=== -- cgit v1.2.1 From 906f19f1365488f90f7473e833a7a13f2c1387ac Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 1 Sep 2015 00:59:31 -0700 Subject: fix test --- ext/zip/tests/bug70350.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/zip/tests/bug70350.phpt b/ext/zip/tests/bug70350.phpt index c308f549cf..d81de65a32 100644 --- a/ext/zip/tests/bug70350.phpt +++ b/ext/zip/tests/bug70350.phpt @@ -15,7 +15,7 @@ $archive->addEmptyDir("../down2/"); $archive->close(); $archive2 = new ZipArchive(); -$archive2->open('a.zip'); +$archive2->open("$dir/a.zip"); $archive2->extractTo($dir); $archive2->close(); var_dump(file_exists("$dir/down2/")); -- cgit v1.2.1 From 1390a5812b151e0ea8f74e64bfeaa5df4dd5b801 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 1 Sep 2015 01:16:30 -0700 Subject: Fix bug #70312 - HAVAL gives wrong hashes in specific cases --- ext/hash/hash_haval.c | 10 +++++----- ext/hash/tests/bug70312.phpt | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 ext/hash/tests/bug70312.phpt diff --git a/ext/hash/hash_haval.c b/ext/hash/hash_haval.c index 32437cecdf..21027438cc 100644 --- a/ext/hash/hash_haval.c +++ b/ext/hash/hash_haval.c @@ -336,7 +336,7 @@ PHP_HASH_API void PHP_HAVAL128Final(unsigned char *digest, PHP_HAVAL_CTX * conte /* Pad out to 118 mod 128. */ - index = (unsigned int) ((context->count[0] >> 3) & 0x3f); + index = (unsigned int) ((context->count[0] >> 3) & 0x7f); padLen = (index < 118) ? (118 - index) : (246 - index); PHP_HAVALUpdate(context, PADDING, padLen); @@ -390,7 +390,7 @@ PHP_HASH_API void PHP_HAVAL160Final(unsigned char *digest, PHP_HAVAL_CTX * conte /* Pad out to 118 mod 128. */ - index = (unsigned int) ((context->count[0] >> 3) & 0x3f); + index = (unsigned int) ((context->count[0] >> 3) & 0x7f); padLen = (index < 118) ? (118 - index) : (246 - index); PHP_HAVALUpdate(context, PADDING, padLen); @@ -444,7 +444,7 @@ PHP_HASH_API void PHP_HAVAL192Final(unsigned char *digest, PHP_HAVAL_CTX * conte /* Pad out to 118 mod 128. */ - index = (unsigned int) ((context->count[0] >> 3) & 0x3f); + index = (unsigned int) ((context->count[0] >> 3) & 0x7f); padLen = (index < 118) ? (118 - index) : (246 - index); PHP_HAVALUpdate(context, PADDING, padLen); @@ -484,7 +484,7 @@ PHP_HASH_API void PHP_HAVAL224Final(unsigned char *digest, PHP_HAVAL_CTX * conte /* Pad out to 118 mod 128. */ - index = (unsigned int) ((context->count[0] >> 3) & 0x3f); + index = (unsigned int) ((context->count[0] >> 3) & 0x7f); padLen = (index < 118) ? (118 - index) : (246 - index); PHP_HAVALUpdate(context, PADDING, padLen); @@ -525,7 +525,7 @@ PHP_HASH_API void PHP_HAVAL256Final(unsigned char *digest, PHP_HAVAL_CTX * conte /* Pad out to 118 mod 128. */ - index = (unsigned int) ((context->count[0] >> 3) & 0x3f); + index = (unsigned int) ((context->count[0] >> 3) & 0x7f); padLen = (index < 118) ? (118 - index) : (246 - index); PHP_HAVALUpdate(context, PADDING, padLen); diff --git a/ext/hash/tests/bug70312.phpt b/ext/hash/tests/bug70312.phpt new file mode 100644 index 0000000000..5ded1ac5a5 --- /dev/null +++ b/ext/hash/tests/bug70312.phpt @@ -0,0 +1,18 @@ +--TEST-- +Bug #70312 HAVAL gives wrong hashes in specific cases +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +string(32) "f3f0d23819b87228b4b70ee350afaa9d" +string(40) "aded6485e137f11d7292212ba3fa961714df0564" +string(48) "e53da2b16269fe732e9a898a96707a9f28404d7333b02286" +string(56) "c574fb307f0817b514b9bb2e7c4bfaffb7ad667aca3c8b523fefcf10" +string(64) "fb73c19300b14d5cb393d929bf005e6c2d459a4c9c009e9813af1d2d3637ee8f" \ No newline at end of file -- cgit v1.2.1 From 7c31203935589ab4fcb104041ef9d87f747bfee4 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 1 Sep 2015 11:38:15 -0700 Subject: Improve fix for #70172 --- ext/standard/tests/serialize/bug70172.phpt | 2 + ext/standard/tests/serialize/bug70172_2.phpt | 68 ++++++++++++++++++++++++++++ ext/standard/var.c | 3 +- 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/serialize/bug70172_2.phpt diff --git a/ext/standard/tests/serialize/bug70172.phpt b/ext/standard/tests/serialize/bug70172.phpt index 0e9d7ed89a..0a4aa4be16 100644 --- a/ext/standard/tests/serialize/bug70172.phpt +++ b/ext/standard/tests/serialize/bug70172.phpt @@ -1,5 +1,7 @@ --TEST-- Bug #70172 - Use After Free Vulnerability in unserialize() +--XFAIL-- +Memory leak on debug build, needs fix. --FILE-- data); + } + function unserialize($data) { + $this->data = unserialize($data); + } +} + +class obj2 { + var $ryat; + function __wakeup() { + $this->ryat = 1; + } +} + +$fakezval = ptr2str(1122334455); +$fakezval .= ptr2str(0); +$fakezval .= "\x00\x00\x00\x00"; +$fakezval .= "\x01"; +$fakezval .= "\x00"; +$fakezval .= "\x00\x00"; + +$inner = 'r:2;'; +$exploit = 'a:2:{i:0;O:4:"obj2":1:{s:4:"ryat";C:3:"obj":'.strlen($inner).':{'.$inner.'}}i:1;a:1:{i:0;a:1:{i:0;R:4;}}}'; + +$data = unserialize($exploit); + +for ($i = 0; $i < 5; $i++) { + $v[$i] = $fakezval.$i; +} + +var_dump($data); + +function ptr2str($ptr) +{ + $out = ''; + for ($i = 0; $i < 8; $i++) { + $out .= chr($ptr & 0xff); + $ptr >>= 8; + } + return $out; +} +?> +--EXPECTF-- +array(2) { + [0]=> + object(obj2)#%d (1) { + ["ryat"]=> + int(1) + } + [1]=> + array(1) { + [0]=> + array(1) { + [0]=> + object(obj2)#%d (1) { + ["ryat"]=> + int(1) + } + } + } +} \ No newline at end of file diff --git a/ext/standard/var.c b/ext/standard/var.c index 33b976f42d..113b8cb66a 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -981,7 +981,8 @@ PHP_FUNCTION(unserialize) *old_rval = *return_value; zval_copy_ctor(old_rval); var_push_dtor_no_addref(&var_hash, &return_value); - var_push_dtor_no_addref(&var_hash, &old_rval); + /* FIXME: old_rval is not freed in some scenarios, see bug #70172 + var_push_dtor_no_addref(&var_hash, &old_rval); */ } else { var_push_dtor(&var_hash, &return_value); } -- cgit v1.2.1 From 7ceb0e3a186782ba007dfd4e867d7eed70437740 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 1 Sep 2015 11:53:59 -0700 Subject: add NEWS for fixes --- NEWS | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 33a818f69b..84e77405a9 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,40 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? 2015 PHP 5.4.45 +03 Sep 2015 PHP 5.4.45 + +- Core: + . Fixed bug #70172 (Use After Free Vulnerability in unserialize()). (Stas) + . Fixed bug #70219 (Use after free vulnerability in session deserializer). + (taoguangchen at icloud dot com) + +- EXIF: + . Fixed bug #70385 (Buffer over-read in exif_read_data with TIFF IFD tag byte + value of 32 bytes). (Stas) + +- hash: + . Fixed bug #70312 (HAVAL gives wrong hashes in specific cases). (letsgolee + at naver dot com) + +- PCRE: + . Fixed bug #70345 (Multiple vulnerabilities related to PCRE functions). + (Anatol Belski) + +- SOAP: + . Fixed bug #70388 (SOAP serialize_function_call() type confusion / RCE). + (Stas) + +- SPL: + . Fixed bug #70365 (Use-after-free vulnerability in unserialize() with + SplObjectStorage). (taoguangchen at icloud dot com) + . Fixed bug #70366 (Use-after-free vulnerability in unserialize() with + SplDoublyLinkedList). (taoguangchen at icloud dot com) + +- XSLT: + . Fixed bug #69782 (NULL pointer dereference). (Stas) + +- ZIP: + . Fixed bug #70350 (ZipArchive::extractTo allows for directory traversal when + creating directories). (neal at fb dot com) 06 Aug 2015 PHP 5.4.44 -- cgit v1.2.1 From 31b634bf7cb8e3de1dfa71418e348133c2365933 Mon Sep 17 00:00:00 2001 From: Stanislav Malyshev Date: Tue, 1 Sep 2015 12:03:48 -0700 Subject: update NEWS --- NEWS | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index a519512f29..a8b3a82c61 100644 --- a/NEWS +++ b/NEWS @@ -1,9 +1,45 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? 2015, PHP 5.5.29 +?? ??? 2015, PHP 5.5.30 ** PHP 5.5 is in security-only mode , please do not commit to this branch ** +03 Sep 2015, PHP 5.5.29 + +- Core: + . Fixed bug #70172 (Use After Free Vulnerability in unserialize()). (Stas) + . Fixed bug #70219 (Use after free vulnerability in session deserializer). + (taoguangchen at icloud dot com) + +- EXIF: + . Fixed bug #70385 (Buffer over-read in exif_read_data with TIFF IFD tag byte + value of 32 bytes). (Stas) + +- hash: + . Fixed bug #70312 (HAVAL gives wrong hashes in specific cases). (letsgolee + at naver dot com) + +- PCRE: + . Fixed bug #70345 (Multiple vulnerabilities related to PCRE functions). + (Anatol Belski) + +- SOAP: + . Fixed bug #70388 (SOAP serialize_function_call() type confusion / RCE). + (Stas) + +- SPL: + . Fixed bug #70365 (Use-after-free vulnerability in unserialize() with + SplObjectStorage). (taoguangchen at icloud dot com) + . Fixed bug #70366 (Use-after-free vulnerability in unserialize() with + SplDoublyLinkedList). (taoguangchen at icloud dot com) + +- XSLT: + . Fixed bug #69782 (NULL pointer dereference). (Stas) + +- ZIP: + . Fixed bug #70350 (ZipArchive::extractTo allows for directory traversal when + creating directories). (neal at fb dot com) + 06 Aug 2015, PHP 5.5.28 - Core: @@ -80,9 +116,10 @@ PHP NEWS (Matteo) - Phar: - . Fixed bug #69958 (Segfault in Phar::convertToData on invalid file). (Stas) + . Fixed bug #69958 (Segfault in Phar::convertToData on invalid file). + (CVE-2015-5589) (Stas) . Fixed bug #69923 (Buffer overflow and stack smashing error in - phar_fix_filepath). (Stas) + phar_fix_filepath). (CVE-2015-5590) (Stas) - SimpleXML: . Refactored the fix for bug #66084 (simplexml_load_string() mangles empty @@ -354,7 +391,8 @@ PHP NEWS - SOAP: . Fixed bug #69085 (SoapClient's __call() type confusion through - unserialize()). (andrea dot palazzo at truel dot it, Laruence) + unserialize()). (CVE-2015-4147, CVE-2015-4148) + (andrea dot palazzo at truel dot it, Laruence) - SPL: . Fixed bug #69108 ("Segmentation fault" when (de)serializing -- cgit v1.2.1