Projects
home:rottame:vhosts-ng:php74
php7
php7-CVE-2025-1735.patch
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File php7-CVE-2025-1735.patch of Package php7
From a2cdff5583ad2bfe9a27fba71e2b1fc423296dc0 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka <bukka@php.net> Date: Tue, 4 Mar 2025 17:23:01 +0100 Subject: [PATCH] Fix GHSA-hrwm-9436-5mv3: pgsql escaping no error checks This adds error checks for escape function is pgsql and pdo_pgsql extensions. It prevents possibility of storing not properly escaped data which could potentially lead to some security issues. --- ext/pdo_pgsql/pgsql_driver.c | 10 +- ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 24 ++++ ext/pgsql/pgsql.c | 126 ++++++++++++++++--- ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 64 ++++++++++ 4 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt create mode 100644 ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt Index: php-7.4.33/ext/pdo_pgsql/pgsql_driver.c =================================================================== --- php-7.4.33.orig/ext/pdo_pgsql/pgsql_driver.c +++ php-7.4.33/ext/pdo_pgsql/pgsql_driver.c @@ -323,11 +323,15 @@ static int pgsql_handle_quoter(pdo_dbh_t unsigned char *escaped; pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; size_t tmp_len; + int err; switch (paramtype) { case PDO_PARAM_LOB: /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ escaped = PQescapeByteaConn(H->server, (unsigned char *)unquoted, unquotedlen, &tmp_len); + if (escaped == NULL) { + return NULL; + } *quotedlen = tmp_len + 1; *quoted = emalloc(*quotedlen + 1); memcpy((*quoted)+1, escaped, *quotedlen-2); @@ -339,7 +343,11 @@ static int pgsql_handle_quoter(pdo_dbh_t default: *quoted = safe_emalloc(2, unquotedlen, 3); (*quoted)[0] = '\''; - *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL); + *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, &err); + if (err) { + efree(quoted); + return NULL; + } (*quoted)[*quotedlen + 1] = '\''; (*quoted)[*quotedlen + 2] = '\0'; *quotedlen += 2; Index: php-7.4.33/ext/pgsql/pgsql.c =================================================================== --- php-7.4.33.orig/ext/pgsql/pgsql.c +++ php-7.4.33/ext/pgsql/pgsql.c @@ -4393,10 +4393,16 @@ PHP_FUNCTION(pg_escape_string) to = zend_string_safe_alloc(ZSTR_LEN(from), 2, 0, 0); #ifdef HAVE_PQESCAPE_CONN if (link) { + int err; if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) { RETURN_FALSE; } - ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL); + ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), &err); + if (err) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escaping string failed"); + zend_string_efree(to); + RETURN_THROWS(); + } } else #endif { @@ -4446,6 +4452,11 @@ PHP_FUNCTION(pg_escape_bytea) #endif to = (char *)PQescapeBytea((unsigned char*)from, from_len, &to_len); + if (to == NULL) { + zend_argument_value_error(ZEND_NUM_ARGS(), "Escape failure"); + RETURN_THROWS(); + } + RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */ PQfreemem(to); } @@ -5529,7 +5540,7 @@ PHP_PGSQL_API int php_pgsql_meta_data(PG char *escaped; smart_str querystr = {0}; size_t new_len; - int i, num_rows; + int i, num_rows, err; zval elem; if (!*table_name) { @@ -5570,7 +5581,14 @@ PHP_PGSQL_API int php_pgsql_meta_data(PG "WHERE a.attnum > 0 AND c.relname = '"); } escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table name '%s' failed", table_name); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -5578,7 +5596,14 @@ PHP_PGSQL_API int php_pgsql_meta_data(PG smart_str_appends(&querystr, "' AND n.nspname = '"); escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1); - new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL); + new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), &err); + if (err) { + php_error_docref(NULL, E_WARNING, "Escaping table namespace '%s' failed", table_name); + efree(src); + efree(escaped); + smart_str_free(&querystr); + return FAILURE; + } if (new_len) { smart_str_appendl(&querystr, escaped, new_len); } @@ -5850,7 +5875,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGco { zend_string *field = NULL; zval meta, *def, *type, *not_null, *has_default, *is_enum, *val, new_val; - int err = 0, skip_field; + int err = 0, escape_err = 0, skip_field; php_pgsql_data_type data_type; assert(pg_link != NULL); @@ -6101,10 +6126,16 @@ PHP_PGSQL_API int php_pgsql_convert(PGco /* PostgreSQL ignores \0 */ str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0); /* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */ - ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); - str = zend_string_truncate(str, ZSTR_LEN(str), 0); - ZVAL_NEW_STR(&new_val, str); - php_pgsql_add_quotes(&new_val, 1); + ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), + Z_STRVAL_P(val), Z_STRLEN_P(val), &escape_err); + if (escape_err) { + err = 1; + } else { + str = zend_string_truncate(str, ZSTR_LEN(str), 0); + ZVAL_NEW_STR(&new_val, str); + php_pgsql_add_quotes(&new_val, 1); + } + } break; @@ -6126,7 +6157,15 @@ PHP_PGSQL_API int php_pgsql_convert(PGco } PGSQL_CONV_CHECK_IGNORE(); if (err) { - php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); + if (escape_err) { + php_error_docref(NULL, E_NOTICE, + "String value escaping failed for PostgreSQL '%s' (%s)", + Z_STRVAL_P(type), ZSTR_VAL(field)); + } else { + php_error_docref(NULL, E_NOTICE, + "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", + Z_STRVAL_P(type), ZSTR_VAL(field)); + } } break; @@ -6406,6 +6445,12 @@ PHP_PGSQL_API int php_pgsql_convert(PGco #else tmp = PQescapeBytea(Z_STRVAL_P(val), (unsigned char *)Z_STRLEN_P(val), &to_len); #endif + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Escaping value failed for %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); + err = 1; + break; + } + ZVAL_STRINGL(&new_val, (char *)tmp, to_len - 1); /* PQescapeBytea's to_len includes additional '\0' */ PQfreemem(tmp); php_pgsql_add_quotes(&new_val, 1); @@ -6488,6 +6533,12 @@ PHP_PGSQL_API int php_pgsql_convert(PGco zend_hash_update(Z_ARRVAL_P(result), field, &new_val); } else { char *escaped = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field)); + if (escaped == NULL) { + /* This cannot fail because of invalid string but only due to failed memory allocation */ + php_error_docref(NULL, E_NOTICE, "Escaping field '%s' failed", ZSTR_VAL(field)); + err = 1; + break; + } add_assoc_zval(result, escaped, &new_val); PGSQLfree(escaped); } @@ -6566,7 +6617,7 @@ static int do_exec(smart_str *querystr, } /* }}} */ -static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const char *table) /* {{{ */ +static inline int build_tablename(smart_str *querystr, PGconn *pg_link, const char *table) /* {{{ */ { size_t table_len = strlen(table); @@ -6577,6 +6628,10 @@ static inline void build_tablename(smart smart_str_appendl(querystr, table, len); } else { char *escaped = PGSQLescapeIdentifier(pg_link, table, len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", table); + return FAILURE; + } smart_str_appends(querystr, escaped); PGSQLfree(escaped); } @@ -6589,11 +6644,17 @@ static inline void build_tablename(smart smart_str_appendl(querystr, after_dot, len); } else { char *escaped = PGSQLescapeIdentifier(pg_link, after_dot, len); + if (escaped == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", table); + return FAILURE; + } smart_str_appendc(querystr, '.'); smart_str_appends(querystr, escaped); PGSQLfree(escaped); } } + + return SUCCESS; } /* }}} */ @@ -6615,7 +6676,9 @@ PHP_PGSQL_API int php_pgsql_insert(PGcon ZVAL_UNDEF(&converted); if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) { smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " DEFAULT VALUES"); goto no_values; @@ -6631,7 +6694,9 @@ PHP_PGSQL_API int php_pgsql_insert(PGcon } smart_str_appends(&querystr, "INSERT INTO "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " ("); ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) { @@ -6641,6 +6706,10 @@ PHP_PGSQL_API int php_pgsql_insert(PGcon } if (opt & PGSQL_DML_ESCAPE) { tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + goto cleanup; + } smart_str_appends(&querystr, tmp); PGSQLfree(tmp); } else { @@ -6652,15 +6721,19 @@ PHP_PGSQL_API int php_pgsql_insert(PGcon smart_str_appends(&querystr, ") VALUES ("); /* make values string */ - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) { + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(var_array), fld, val) { /* we can avoid the key_type check here, because we tested it in the other loop */ switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { - size_t new_len; - char *tmp; - tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + int error; + char *tmp = safe_emalloc(Z_STRLEN_P(val), 2, 1); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + goto cleanup; + } smart_str_appendc(&querystr, '\''); smart_str_appendl(&querystr, tmp, new_len); smart_str_appendc(&querystr, '\''); @@ -6810,6 +6883,10 @@ static inline int build_assignment_strin } if (opt & PGSQL_DML_ESCAPE) { char *tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); + if (tmp == NULL) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); + return -1; + } smart_str_appends(querystr, tmp); PGSQLfree(tmp); } else { @@ -6824,8 +6901,14 @@ static inline int build_assignment_strin switch (Z_TYPE_P(val)) { case IS_STRING: if (opt & PGSQL_DML_ESCAPE) { + int error; char *tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); - size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); + size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); + if (error) { + php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); + efree(tmp); + return -1; + } smart_str_appendc(querystr, '\''); smart_str_appendl(querystr, tmp, new_len); smart_str_appendc(querystr, '\''); @@ -6894,7 +6977,9 @@ PHP_PGSQL_API int php_pgsql_update(PGcon } smart_str_appends(&querystr, "UPDATE "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " SET "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt)) @@ -6992,7 +7077,9 @@ PHP_PGSQL_API int php_pgsql_delete(PGcon } smart_str_appends(&querystr, "DELETE FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " WHERE "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) @@ -7130,7 +7217,9 @@ PHP_PGSQL_API int php_pgsql_result2array } smart_str_appends(&querystr, "SELECT * FROM "); - build_tablename(&querystr, pg_link, table); + if (build_tablename(&querystr, pg_link, table) == FAILURE) { + goto cleanup; + } smart_str_appends(&querystr, " WHERE "); if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt))
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.