diff options
Diffstat (limited to 'bug72479.patch')
-rw-r--r-- | bug72479.patch | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/bug72479.patch b/bug72479.patch new file mode 100644 index 0000000..6d459be --- /dev/null +++ b/bug72479.patch @@ -0,0 +1,408 @@ +From cab1c3b3708eead315e033359d07049b23b147a3 Mon Sep 17 00:00:00 2001 +From: Stanislav Malyshev <stas@php.net> +Date: Sun, 26 Jun 2016 17:52:09 -0700 +Subject: [PATCH] Fixed bug #72479 - same as #72434 + +--- + ext/snmp/snmp.c | 89 ++++++++++++++++++++++++-------------------- + ext/snmp/tests/bug72479.phpt | 35 +++++++++++++++++ + 2 files changed, 84 insertions(+), 40 deletions(-) + create mode 100644 ext/snmp/tests/bug72479.phpt + +diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c +index 6c1da4c..e1161c7 100644 +--- a/ext/snmp/snmp.c ++++ b/ext/snmp/snmp.c +@@ -475,7 +475,7 @@ static void php_snmp_session_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC) + static void php_snmp_object_free_storage(void *object TSRMLS_DC) + { + php_snmp_object *intern = (php_snmp_object *)object; +- ++ + if (!intern) { + return; + } +@@ -483,7 +483,7 @@ static void php_snmp_object_free_storage(void *object TSRMLS_DC) + netsnmp_session_free(&(intern->session)); + + zend_object_std_dtor(&intern->zo TSRMLS_CC); +- ++ + efree(intern); + } + +@@ -503,7 +503,7 @@ static zend_object_value php_snmp_object_new(zend_class_entry *class_type TSRMLS + retval.handlers = (zend_object_handlers *) &php_snmp_object_handlers; + + return retval; +- ++ + } + + /* {{{ php_snmp_error +@@ -556,7 +556,7 @@ static void php_snmp_getvalue(struct variable_list *vars, zval *snmpval TSRMLS_D + char *dbuf = (char *)NULL; + int buflen = sizeof(sbuf) - 1; + int val_len = vars->val_len; +- ++ + /* use emalloc() for large values, use static array otherwize */ + + /* There is no way to know the size of buffer snprint_value() needs in order to print a value there. +@@ -702,7 +702,7 @@ static void php_snmp_getvalue(struct variable_list *vars, zval *snmpval TSRMLS_D + * SNMP object fetcher/setter for all SNMP versions + * + */ +-static void php_snmp_internal(INTERNAL_FUNCTION_PARAMETERS, int st, ++static void php_snmp_internal(INTERNAL_FUNCTION_PARAMETERS, int st, + struct snmp_session *session, + struct objid_query *objid_query) + { +@@ -721,7 +721,7 @@ static void php_snmp_internal(INTERNAL_FUNCTION_PARAMETERS, int st, + + /* we start with retval=FALSE. If any actual data is acquired, retval will be set to appropriate type */ + RETVAL_FALSE; +- ++ + /* reset errno and errstr */ + php_snmp_error(getThis(), NULL TSRMLS_CC, PHP_SNMP_ERRNO_NOERROR, ""); + +@@ -805,8 +805,8 @@ retry: + } + for (vars = response->variables; vars; vars = vars->next_variable) { + /* do not output errors as values */ +- if ( vars->type == SNMP_ENDOFMIBVIEW || +- vars->type == SNMP_NOSUCHOBJECT || ++ if ( vars->type == SNMP_ENDOFMIBVIEW || ++ vars->type == SNMP_NOSUCHOBJECT || + vars->type == SNMP_NOSUCHINSTANCE ) { + if ((st & SNMP_CMD_WALK) && Z_TYPE_P(return_value) == IS_ARRAY) { + break; +@@ -816,8 +816,8 @@ retry: + php_snmp_error(getThis(), NULL TSRMLS_CC, PHP_SNMP_ERRNO_ERROR_IN_REPLY, "Error in packet at '%s': %s", buf, buf2); + continue; + } +- +- if ((st & SNMP_CMD_WALK) && ++ ++ if ((st & SNMP_CMD_WALK) && + (vars->name_length < rootlen || memcmp(root, vars->name, rootlen * sizeof(oid)))) { /* not part of this subtree */ + if (Z_TYPE_P(return_value) == IS_ARRAY) { /* some records are fetched already, shut down further lookup */ + keepwalking = 0; +@@ -1101,7 +1101,7 @@ static int php_snmp_parse_oid(zval *object, int st, struct objid_query *objid_qu + efree(objid_query->vars); + return FALSE; + } +- } else { ++ } else { + memmove((char *)objid_query->vars[0].name, (char *)objid_mib, sizeof(objid_mib)); + objid_query->vars[0].name_length = sizeof(objid_mib) / sizeof(oid); + } +@@ -1437,7 +1437,7 @@ static void php_snmp(INTERNAL_FUNCTION_PARAMETERS, int st, int version) + int session_less_mode = (getThis() == NULL); + php_snmp_object *snmp_object; + php_snmp_object glob_snmp_object; +- ++ + objid_query.max_repetitions = -1; + objid_query.non_repeaters = 0; + objid_query.valueretrieval = SNMP_G(valueretrieval); +@@ -1550,7 +1550,7 @@ static void php_snmp(INTERNAL_FUNCTION_PARAMETERS, int st, int version) + } + + php_snmp_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, st, session, &objid_query); +- ++ + efree(objid_query.vars); + + if (session_less_mode) { +@@ -1563,7 +1563,7 @@ static void php_snmp(INTERNAL_FUNCTION_PARAMETERS, int st, int version) + } + /* }}} */ + +-/* {{{ proto mixed snmpget(string host, string community, mixed object_id [, int timeout [, int retries]]) ++/* {{{ proto mixed snmpget(string host, string community, mixed object_id [, int timeout [, int retries]]) + Fetch a SNMP object */ + PHP_FUNCTION(snmpget) + { +@@ -1571,7 +1571,7 @@ PHP_FUNCTION(snmpget) + } + /* }}} */ + +-/* {{{ proto mixed snmpgetnext(string host, string community, mixed object_id [, int timeout [, int retries]]) ++/* {{{ proto mixed snmpgetnext(string host, string community, mixed object_id [, int timeout [, int retries]]) + Fetch a SNMP object */ + PHP_FUNCTION(snmpgetnext) + { +@@ -1579,7 +1579,7 @@ PHP_FUNCTION(snmpgetnext) + } + /* }}} */ + +-/* {{{ proto mixed snmpwalk(string host, string community, mixed object_id [, int timeout [, int retries]]) ++/* {{{ proto mixed snmpwalk(string host, string community, mixed object_id [, int timeout [, int retries]]) + Return all objects under the specified object id */ + PHP_FUNCTION(snmpwalk) + { +@@ -1595,7 +1595,7 @@ PHP_FUNCTION(snmprealwalk) + } + /* }}} */ + +-/* {{{ proto bool snmpset(string host, string community, mixed object_id, mixed type, mixed value [, int timeout [, int retries]]) ++/* {{{ proto bool snmpset(string host, string community, mixed object_id, mixed type, mixed value [, int timeout [, int retries]]) + Set the value of a SNMP object */ + PHP_FUNCTION(snmpset) + { +@@ -1642,7 +1642,7 @@ PHP_FUNCTION(snmp_set_enum_print) + + netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_ENUM, (int) a1); + RETURN_TRUE; +-} ++} + /* }}} */ + + /* {{{ proto bool snmp_set_oid_output_format(int oid_format) +@@ -1670,10 +1670,10 @@ PHP_FUNCTION(snmp_set_oid_output_format) + RETURN_FALSE; + break; + } +-} ++} + /* }}} */ + +-/* {{{ proto mixed snmp2_get(string host, string community, mixed object_id [, int timeout [, int retries]]) ++/* {{{ proto mixed snmp2_get(string host, string community, mixed object_id [, int timeout [, int retries]]) + Fetch a SNMP object */ + PHP_FUNCTION(snmp2_get) + { +@@ -1681,7 +1681,7 @@ PHP_FUNCTION(snmp2_get) + } + /* }}} */ + +-/* {{{ proto mixed snmp2_getnext(string host, string community, mixed object_id [, int timeout [, int retries]]) ++/* {{{ proto mixed snmp2_getnext(string host, string community, mixed object_id [, int timeout [, int retries]]) + Fetch a SNMP object */ + PHP_FUNCTION(snmp2_getnext) + { +@@ -1689,7 +1689,7 @@ PHP_FUNCTION(snmp2_getnext) + } + /* }}} */ + +-/* {{{ proto mixed snmp2_walk(string host, string community, mixed object_id [, int timeout [, int retries]]) ++/* {{{ proto mixed snmp2_walk(string host, string community, mixed object_id [, int timeout [, int retries]]) + Return all objects under the specified object id */ + PHP_FUNCTION(snmp2_walk) + { +@@ -1705,7 +1705,7 @@ PHP_FUNCTION(snmp2_real_walk) + } + /* }}} */ + +-/* {{{ proto bool snmp2_set(string host, string community, mixed object_id, mixed type, mixed value [, int timeout [, int retries]]) ++/* {{{ proto bool snmp2_set(string host, string community, mixed object_id, mixed type, mixed value [, int timeout [, int retries]]) + Set the value of a SNMP object */ + PHP_FUNCTION(snmp2_set) + { +@@ -1821,7 +1821,7 @@ PHP_METHOD(snmp, __construct) + + snmp_object = (php_snmp_object *)zend_object_store_get_object(object TSRMLS_CC); + zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC); +- ++ + if (zend_parse_parameters(argc TSRMLS_CC, "lss|ll", &version, &a1, &a1_len, &a2, &a2_len, &timeout, &retries) == FAILURE) { + zend_restore_error_handling(&error_handling TSRMLS_CC); + return; +@@ -1843,7 +1843,7 @@ PHP_METHOD(snmp, __construct) + if (snmp_object->session) { + netsnmp_session_free(&(snmp_object->session)); + } +- ++ + if (netsnmp_session_init(&(snmp_object->session), version, a1, a2, timeout, retries TSRMLS_CC)) { + return; + } +@@ -1857,7 +1857,7 @@ PHP_METHOD(snmp, __construct) + } + /* }}} */ + +-/* {{{ proto bool SNMP::close() ++/* {{{ proto bool SNMP::close() + Close SNMP session */ + PHP_METHOD(snmp, close) + { +@@ -1900,7 +1900,7 @@ PHP_METHOD(snmp, walk) + } + /* }}} */ + +-/* {{{ proto bool SNMP::set(mixed object_id, mixed type, mixed value) ++/* {{{ proto bool SNMP::set(mixed object_id, mixed type, mixed value) + Set the value of a SNMP object */ + PHP_METHOD(snmp, set) + { +@@ -1918,7 +1918,7 @@ PHP_METHOD(snmp, setSecurity) + int argc = ZEND_NUM_ARGS(); + + snmp_object = (php_snmp_object *)zend_object_store_get_object(object TSRMLS_CC); +- ++ + if (zend_parse_parameters(argc TSRMLS_CC, "s|ssssss", &a1, &a1_len, &a2, &a2_len, &a3, &a3_len, + &a4, &a4_len, &a5, &a5_len, &a6, &a6_len, &a7, &a7_len) == FAILURE) { + RETURN_FALSE; +@@ -1932,7 +1932,7 @@ PHP_METHOD(snmp, setSecurity) + } + /* }}} */ + +-/* {{{ proto long SNMP::getErrno() ++/* {{{ proto long SNMP::getErrno() + Get last error code number */ + PHP_METHOD(snmp, getErrno) + { +@@ -1946,7 +1946,7 @@ PHP_METHOD(snmp, getErrno) + } + /* }}} */ + +-/* {{{ proto long SNMP::getError() ++/* {{{ proto long SNMP::getError() + Get last error message */ + PHP_METHOD(snmp, getError) + { +@@ -2095,6 +2095,14 @@ static int php_snmp_has_property(zval *object, zval *member, int has_set_exists, + } + /* }}} */ + ++static HashTable *php_snmp_get_gc(zval *object, zval ***gc_data, int *gc_data_count TSRMLS_DC) /* {{{ */ ++{ ++ *gc_data = NULL; ++ *gc_data_count = 0; ++ return zend_std_get_properties(object TSRMLS_CC); ++} ++/* }}} */ ++ + /* {{{ php_snmp_get_properties(zval *object) + Returns all object properties. Injects SNMP properties into object on first call */ + static HashTable *php_snmp_get_properties(zval *object TSRMLS_DC) +@@ -2137,23 +2145,23 @@ static int php_snmp_read_info(php_snmp_object *snmp_object, zval **retval TSRMLS + if (snmp_object->session == NULL) { + return SUCCESS; + } +- ++ + MAKE_STD_ZVAL(val); + ZVAL_STRINGL(val, snmp_object->session->peername, strlen(snmp_object->session->peername), 1); + add_assoc_zval(*retval, "hostname", val); +- ++ + MAKE_STD_ZVAL(val); + ZVAL_LONG(val, snmp_object->session->remote_port); + add_assoc_zval(*retval, "port", val); +- ++ + MAKE_STD_ZVAL(val); + ZVAL_LONG(val, snmp_object->session->timeout); + add_assoc_zval(*retval, "timeout", val); +- ++ + MAKE_STD_ZVAL(val); + ZVAL_LONG(val, snmp_object->session->retries); + add_assoc_zval(*retval, "retries", val); +- ++ + return SUCCESS; + } + /* }}} */ +@@ -2226,7 +2234,7 @@ static int php_snmp_write_max_oids(php_snmp_object *snmp_object, zval *newval TS + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "max_oids should be positive integer or NULL, got %ld", Z_LVAL_P(newval)); + } +- ++ + if (newval == &ztmp) { + zval_dtor(newval); + } +@@ -2254,7 +2262,7 @@ static int php_snmp_write_valueretrieval(php_snmp_object *snmp_object, zval *new + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown SNMP value retrieval method '%ld'", Z_LVAL_P(newval)); + ret = FAILURE; + } +- ++ + if (newval == &ztmp) { + zval_dtor(newval); + } +@@ -2297,7 +2305,7 @@ static int php_snmp_write_oid_output_format(php_snmp_object *snmp_object, zval * + convert_to_long(&ztmp); + newval = &ztmp; + } +- ++ + switch(Z_LVAL_P(newval)) { + case NETSNMP_OID_OUTPUT_SUFFIX: + case NETSNMP_OID_OUTPUT_MODULE: +@@ -2332,7 +2340,7 @@ static int php_snmp_write_exceptions_enabled(php_snmp_object *snmp_object, zval + newval = &ztmp; + } + +- snmp_object->exceptions_enabled = Z_LVAL_P(newval); ++ snmp_object->exceptions_enabled = Z_LVAL_P(newval); + + if (newval == &ztmp) { + zval_dtor(newval); +@@ -2401,6 +2409,7 @@ PHP_MINIT_FUNCTION(snmp) + php_snmp_object_handlers.write_property = php_snmp_write_property; + php_snmp_object_handlers.has_property = php_snmp_has_property; + php_snmp_object_handlers.get_properties = php_snmp_get_properties; ++ php_snmp_object_handlers.get_gc = php_snmp_get_gc; + + /* Register SNMP Class */ + INIT_CLASS_ENTRY(ce, "SNMP", php_snmp_class_methods); +@@ -2467,7 +2476,7 @@ PHP_MINIT_FUNCTION(snmp) + PHP_MSHUTDOWN_FUNCTION(snmp) + { + snmp_shutdown("snmpapp"); +- ++ + zend_hash_destroy(&php_snmp_properties); + + return SUCCESS; +diff --git a/ext/snmp/tests/bug72479.phpt b/ext/snmp/tests/bug72479.phpt +new file mode 100644 +index 0000000..0308754 +--- /dev/null ++++ b/ext/snmp/tests/bug72479.phpt +@@ -0,0 +1,35 @@ ++--TEST-- ++Bug #72479: Use After Free Vulnerability in SNMP with GC and unserialize() ++--SKIPIF-- ++<?php ++require_once(dirname(__FILE__).'/skipif.inc'); ++?> ++--FILE-- ++<?php ++$arr = [1, [1, 2, 3, 4, 5], 3, 4, 5]; ++$poc = 'a:3:{i:1;N;i:2;O:4:"snmp":1:{s:11:"quick_print";'.serialize($arr).'}i:1;R:7;}'; ++$out = unserialize($poc); ++gc_collect_cycles(); ++$fakezval = ptr2str(1122334455); ++$fakezval .= ptr2str(0); ++$fakezval .= "\x00\x00\x00\x00"; ++$fakezval .= "\x01"; ++$fakezval .= "\x00"; ++$fakezval .= "\x00\x00"; ++for ($i = 0; $i < 5; $i++) { ++ $v[$i] = $fakezval.$i; ++} ++var_dump($out[1]); ++ ++function ptr2str($ptr) ++{ ++ $out = ''; ++ for ($i = 0; $i < 8; $i++) { ++ $out .= chr($ptr & 0xff); ++ $ptr >>= 8; ++ } ++ return $out; ++} ++?> ++--EXPECT-- ++int(1) +\ No newline at end of file +-- +2.1.4 + |