diff --git a/NEWS b/NEWS index c1bf79e07fba..1b2e879fcaf5 100644 --- a/NEWS +++ b/NEWS @@ -52,6 +52,11 @@ PHP NEWS replacement when a \k backref has no closing delimiter. (Ilia Alshanetsky) +- ODBC: + . Fixed odbc_field_len(), odbc_field_scale() and odbc_field_type() + returning uninitialized memory when SQLColAttribute fails. + (Ilia Alshanetsky) + - PCNTL: . Fixed the declared signature of pcntl_signal(), whose $restart_syscalls argument accepts null and defaults to it. (lacatoire) @@ -78,6 +83,8 @@ PHP NEWS - SOAP: . Fixed WSDL cache corruption when a soap:header defines headerfaults. (Ilia Alshanetsky) + . Fixed stack overflow when parsing a WSDL with self-referential schema + groups or attributeGroups. (Ilia Alshanetsky) - Sodium: . Added support for the libsodium 1.0.22 KEM APIs (X-Wing and ML-KEM768). diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 5a1a20e10686..d1cf2716cfca 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -676,6 +676,7 @@ void odbc_bindcols(odbc_result *result) result->values[i].value_max_len = 0; colfieldid = SQL_COLUMN_DISPLAY_SIZE; + result->values[i].name[0] = '\0'; rc = SQLColAttribute(result->stmt, (SQLUSMALLINT)(i+1), SQL_DESC_NAME, result->values[i].name, sizeof(result->values[i].name), &colnamelen, 0); result->values[i].coltype = 0; @@ -784,10 +785,30 @@ void odbc_transact(INTERNAL_FUNCTION_PARAMETERS, int type) } /* }}} */ +static void odbc_colattribute_failed(odbc_result *result, zend_long pv_num) +{ +#if defined(ODBCVER) && (ODBCVER >= 0x0300) + SQLINTEGER diag_error; + SQLCHAR diag_state[6]; + SQLCHAR diag_text[128]; + + memset(diag_state, '\0', sizeof(diag_state)); + memset(diag_text, '\0', sizeof(diag_text)); + if (SQL_SUCCESS == SQLGetDiagRec(SQL_HANDLE_STMT, result->stmt, 1, diag_state, &diag_error, diag_text, sizeof(diag_text), NULL)) { + diag_state[sizeof(diag_state) - 1] = '\0'; + diag_text[sizeof(diag_text) - 1] = '\0'; + php_error_docref(NULL, E_WARNING, "SQLColAttribute failed for field #%d: [%s] %s", (int)pv_num, diag_state, diag_text); + return; + } +#endif + php_error_docref(NULL, E_WARNING, "SQLColAttribute failed for field #%d", (int)pv_num); +} + /* {{{ odbc_column_lengths */ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type) { odbc_result *result; + RETCODE rc; SQLLEN len; zend_long pv_num; @@ -813,7 +834,11 @@ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type) RETURN_FALSE; } - SQLColAttribute(result->stmt, (SQLUSMALLINT)pv_num, (SQLUSMALLINT) (type?SQL_COLUMN_SCALE:SQL_COLUMN_PRECISION), NULL, 0, NULL, &len); + rc = SQLColAttribute(result->stmt, (SQLUSMALLINT)pv_num, (SQLUSMALLINT)(type ? SQL_COLUMN_SCALE : SQL_COLUMN_PRECISION), NULL, 0, NULL, &len); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + odbc_colattribute_failed(result, pv_num); + len = 0; + } RETURN_LONG(len); } @@ -2315,6 +2340,7 @@ PHP_FUNCTION(odbc_field_type) odbc_result *result; char tmp[32]; SQLSMALLINT tmplen; + RETCODE rc; zend_long pv_num; ZEND_PARSE_PARAMETERS_START(2, 2) @@ -2339,7 +2365,13 @@ PHP_FUNCTION(odbc_field_type) RETURN_FALSE; } - SQLColAttribute(result->stmt, (SQLUSMALLINT)pv_num, SQL_COLUMN_TYPE_NAME, tmp, 31, &tmplen, NULL); + tmp[0] = '\0'; + rc = SQLColAttribute(result->stmt, (SQLUSMALLINT)pv_num, SQL_COLUMN_TYPE_NAME, tmp, sizeof(tmp) - 1, &tmplen, NULL); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + odbc_colattribute_failed(result, pv_num); + RETURN_FALSE; + } + RETURN_STRING(tmp); } /* }}} */ diff --git a/ext/session/tests/user_session_module/gh23043.phpt b/ext/session/tests/user_session_module/gh23043.phpt index e3528884a79a..caf98ac93857 100644 --- a/ext/session/tests/user_session_module/gh23043.phpt +++ b/ext/session/tests/user_session_module/gh23043.phpt @@ -27,9 +27,9 @@ string(0) "" Warning: SessionHandler::write(): Session ID is too long or contains illegal characters. Only the A-Z, a-z, 0-9, "-", and "," characters are allowed in %s on line %d -Warning: session_write_close(): Failed to write session data using user defined save handler. (session.save_path: , handler: a::write) in %s on line %d +Warning: session_write_close(): Failed to write session data using user defined save handler. (session.save_path: %S, handler: a::write) in %s on line %d string(0) "" Warning: SessionHandler::write(): Session ID is too long or contains illegal characters. Only the A-Z, a-z, 0-9, "-", and "," characters are allowed in Unknown on line 0 -Warning: session_write_close(): Failed to write session data using user defined save handler. (session.save_path: , handler: a::write) in Unknown on line 0 +Warning: session_write_close(): Failed to write session data using user defined save handler. (session.save_path: %S, handler: a::write) in Unknown on line 0 diff --git a/ext/soap/php_schema.c b/ext/soap/php_schema.c index b675b97b469f..195c5abe45eb 100644 --- a/ext/soap/php_schema.c +++ b/ext/soap/php_schema.c @@ -2193,6 +2193,10 @@ static void schema_attributegroup_fixup(sdlCtx *ctx, sdlAttributePtr attr, HashT if (ctx->attributeGroups != NULL) { tmp = (sdlTypePtr)schema_find_by_ref(ctx->attributeGroups, attr->ref); if (tmp) { + if (zend_hash_index_find_ptr(&ctx->fixupInProgress, (zend_ulong)(uintptr_t)tmp) != NULL) { + soap_error1(E_ERROR, "Parsing Schema: recursive attributeGroup 'ref' attribute '%s'", attr->ref); + } + zend_hash_index_add_ptr(&ctx->fixupInProgress, (zend_ulong)(uintptr_t)tmp, tmp); if (tmp->attributes) { zend_hash_internal_pointer_reset(tmp->attributes); while ((tmp_attr = zend_hash_get_current_data_ptr(tmp->attributes)) != NULL) { @@ -2228,6 +2232,7 @@ static void schema_attributegroup_fixup(sdlCtx *ctx, sdlAttributePtr attr, HashT } } } + zend_hash_index_del(&ctx->fixupInProgress, (zend_ulong)(uintptr_t)tmp); } } efree(attr->ref); @@ -2242,6 +2247,9 @@ static void schema_content_model_fixup(sdlCtx *ctx, sdlContentModelPtr model) sdlTypePtr tmp; if (ctx->sdl->groups && (tmp = zend_hash_str_find_ptr(ctx->sdl->groups, model->u.group_ref, strlen(model->u.group_ref))) != NULL) { + if (zend_hash_index_find_ptr(&ctx->fixupInProgress, (zend_ulong)(uintptr_t)tmp) != NULL) { + soap_error1(E_ERROR, "Parsing Schema: recursive group 'ref' attribute '%s'", model->u.group_ref); + } schema_type_fixup(ctx, tmp); efree(model->u.group_ref); model->kind = XSD_CONTENT_GROUP; @@ -2285,6 +2293,8 @@ static void schema_type_fixup(sdlCtx *ctx, sdlTypePtr type) sdlTypePtr tmp; sdlAttributePtr attr; + zend_hash_index_add_ptr(&ctx->fixupInProgress, (zend_ulong)(uintptr_t)type, type); + if (type->ref != NULL) { if (ctx->sdl->elements != NULL) { tmp = (sdlTypePtr)schema_find_by_ref(ctx->sdl->elements, type->ref); @@ -2337,6 +2347,7 @@ static void schema_type_fixup(sdlCtx *ctx, sdlTypePtr type) } } } + zend_hash_index_del(&ctx->fixupInProgress, (zend_ulong)(uintptr_t)type); } void schema_pass2(sdlCtx *ctx) @@ -2345,6 +2356,8 @@ void schema_pass2(sdlCtx *ctx) sdlAttributePtr attr; sdlTypePtr type; + zend_hash_init(&ctx->fixupInProgress, 0, NULL, NULL, 0); + if (ctx->attributes) { ZEND_HASH_FOREACH_PTR(ctx->attributes, attr) { schema_attribute_fixup(ctx, attr); @@ -2378,6 +2391,8 @@ void schema_pass2(sdlCtx *ctx) zend_hash_destroy(ctx->attributeGroups); efree(ctx->attributeGroups); } + + zend_hash_destroy(&ctx->fixupInProgress); } void delete_model(zval *zv) diff --git a/ext/soap/php_sdl.h b/ext/soap/php_sdl.h index 7c9d8ce7382b..7249f094f165 100644 --- a/ext/soap/php_sdl.h +++ b/ext/soap/php_sdl.h @@ -71,6 +71,7 @@ typedef struct sdlCtx { HashTable *attributes; /* array of sdlAttributePtr */ HashTable *attributeGroups; /* array of sdlTypesPtr */ + HashTable fixupInProgress; php_stream_context *context; zval old_header; } sdlCtx; diff --git a/ext/soap/tests/schema-selfref-attrgroup.phpt b/ext/soap/tests/schema-selfref-attrgroup.phpt new file mode 100644 index 000000000000..668826ea6c39 --- /dev/null +++ b/ext/soap/tests/schema-selfref-attrgroup.phpt @@ -0,0 +1,44 @@ +--TEST-- +SOAP XML Schema: self-referential attributeGroup fix-up recursion is rejected +--EXTENSIONS-- +soap +--FILE-- + + + + + + + + + + + + + + + + + + + +'; +$file = __DIR__ . '/schema-selfref-attrgroup.wsdl'; +file_put_contents($file, $wsdl); +try { + $c = new SoapClient($file, ['exceptions' => true, 'cache_wsdl' => WSDL_CACHE_NONE]); + echo "parsed ok\n"; +} catch (Throwable $e) { + echo $e::class, ": ", substr($e->getMessage(), 0, 120), "\n"; +} +echo "done\n"; +?> +--CLEAN-- + +--EXPECTF-- +SoapFault: SOAP-ERROR: Parsing Schema: recursive attributeGroup 'ref' attribute '%s' +done diff --git a/ext/soap/tests/schema-selfref-group.phpt b/ext/soap/tests/schema-selfref-group.phpt new file mode 100644 index 000000000000..be9d7df524f9 --- /dev/null +++ b/ext/soap/tests/schema-selfref-group.phpt @@ -0,0 +1,46 @@ +--TEST-- +SOAP XML Schema: self-referential group fix-up recursion is rejected +--EXTENSIONS-- +soap +--FILE-- + + + + + + + + + + + + + + + + + + + + + +'; +$file = __DIR__ . '/schema-selfref-group.wsdl'; +file_put_contents($file, $wsdl); +try { + $c = new SoapClient($file, ['exceptions' => true, 'cache_wsdl' => WSDL_CACHE_NONE]); + echo "parsed ok\n"; +} catch (Throwable $e) { + echo $e::class, ": ", substr($e->getMessage(), 0, 120), "\n"; +} +echo "done\n"; +?> +--CLEAN-- + +--EXPECTF-- +SoapFault: SOAP-ERROR: Parsing Schema: recursive group 'ref' attribute '%s' +done