From 845aa58a97777a9249e78896c9cd90e3200241d6 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Mon, 11 May 2026 08:42:46 +0200 Subject: fix patch name --- php-cve-2025-14179.patch | 274 +++++++++++++++++++++++++++++++++++++++++++++++ php-cve-2026-14179.patch | 274 ----------------------------------------------- php.spec | 2 +- 3 files changed, 275 insertions(+), 275 deletions(-) create mode 100644 php-cve-2025-14179.patch delete mode 100644 php-cve-2026-14179.patch diff --git a/php-cve-2025-14179.patch b/php-cve-2025-14179.patch new file mode 100644 index 0000000..71e15cd --- /dev/null +++ b/php-cve-2025-14179.patch @@ -0,0 +1,274 @@ +From 4b0dd469bbba7bf5f25f1a4f694aeb15c3515be4 Mon Sep 17 00:00:00 2001 +From: Saki Takamachi +Date: Sun, 3 May 2026 19:56:30 +0200 +Subject: [PATCH 07/10] GHSA-w476-322c-wpvm: [pdo_firebird] Fix SQL injection + via NUL bytes in quoted strings + +Fixes GHSA-w476-322c-wpvm +Fixes CVE-2025-14179 + +(cherry picked from commit 3f40b65323dd1b85e9bab6878237d3867e449d5c) +--- + ext/pdo_firebird/firebird_driver.c | 69 ++++++++++++------- + .../tests/ghsa-w476-322c-wpvm.phpt | 44 ++++++++++++ + 2 files changed, 88 insertions(+), 25 deletions(-) + create mode 100644 ext/pdo_firebird/tests/ghsa-w476-322c-wpvm.phpt + +diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c +index a446622c90e..25882919f86 100644 +--- a/ext/pdo_firebird/firebird_driver.c ++++ b/ext/pdo_firebird/firebird_driver.c +@@ -291,7 +291,7 @@ static FbTokenType getToken(const char** begin, const char* end) + return ret; + } + +-int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) ++int preprocess(const zend_string* sql, char* sql_out, size_t* sql_out_len, HashTable* named_params) + { + bool passAsIs = 1, execBlock = 0; + zend_long pindex = -1; +@@ -322,7 +322,7 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) + if (l > 252) { + return 0; + } +- strncpy(ident, i, l); ++ memcpy(ident, i, l); + ident[l] = '\0'; + if (!strcasecmp(ident, "EXECUTE")) + { +@@ -347,7 +347,7 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) + if (l > 252) { + return 0; + } +- strncpy(ident2, i2, l); ++ memcpy(ident2, i2, l); + ident2[l] = '\0'; + execBlock = !strcasecmp(ident2, "BLOCK"); + passAsIs = 0; +@@ -363,11 +363,15 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) + + if (passAsIs) + { +- strcpy(sql_out, ZSTR_VAL(sql)); ++ memcpy(sql_out, ZSTR_VAL(sql), ZSTR_LEN(sql)); ++ sql_out[ZSTR_LEN(sql)] = '\0'; ++ *sql_out_len = ZSTR_LEN(sql); + return 1; + } + +- strncat(sql_out, start, p - start); ++ char *sql_out_p = sql_out; ++ memcpy(sql_out_p, start, p - start); ++ sql_out_p += p - start; + + while (p < end) + { +@@ -375,10 +379,12 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) + tok = getToken(&p, end); + switch (tok) + { +- case ttParamMark: +- tok = getToken(&p, end); ++ case ttParamMark: { ++ const char* p_peek = p; ++ tok = getToken(&p_peek, end); + if (tok == ttIdent /*|| tok == ttString*/) + { ++ p = p_peek; + ++pindex; + l = p - start; + /* check the length of the identifier */ +@@ -387,7 +393,7 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) + if (l > 253) { + return 0; + } +- strncpy(pname, start, l); ++ memcpy(pname, start, l); + pname[l] = '\0'; + + if (named_params) { +@@ -396,7 +402,7 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) + zend_hash_str_update(named_params, pname, l, &tmp); + } + +- strcat(sql_out, "?"); ++ *sql_out_p++ = '?'; + } + else + { +@@ -406,10 +412,11 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) + return 0; + } + ++pindex; +- strncat(sql_out, start, p - start); ++ memcpy(sql_out_p, start, p - start); ++ sql_out_p += p - start; + } + break; +- ++ } + case ttIdent: + if (execBlock) + { +@@ -421,11 +428,14 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) + if (l > 252) { + return 0; + } +- strncpy(ident, start, l); ++ memcpy(ident, start, l); + ident[l] = '\0'; + if (!strcasecmp(ident, "AS")) + { +- strncat(sql_out, start, end - start); ++ memcpy(sql_out_p, start, end - start); ++ sql_out_p += end - start; ++ *sql_out_p = '\0'; ++ *sql_out_len = sql_out_p - sql_out; + return 1; + } + } +@@ -436,7 +446,8 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) + case ttComment: + case ttString: + case ttOther: +- strncat(sql_out, start, p - start); ++ memcpy(sql_out_p, start, p - start); ++ sql_out_p += p - start; + break; + + case ttBrokenComment: +@@ -454,6 +465,8 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) + break; + } + } ++ *sql_out_p = '\0'; ++ *sql_out_len = sql_out_p - sql_out; + return 1; + } + +@@ -663,7 +676,7 @@ free_statement: + static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) + { + size_t qcount = 0; +- char const *co, *l, *r; ++ char const *co, *l; + char *c; + size_t quotedlen; + zend_string *quoted_str; +@@ -672,9 +685,15 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un + return zend_string_init("''", 2, 0); + } + ++ const char * const end = ZSTR_VAL(unquoted) + ZSTR_LEN(unquoted); ++ + /* Firebird only requires single quotes to be doubled if string lengths are used */ + /* count the number of ' characters */ +- for (co = ZSTR_VAL(unquoted); (co = strchr(co,'\'')); qcount++, co++); ++ for (co = ZSTR_VAL(unquoted); co < end; co++) { ++ if (*co == '\'') { ++ qcount++; ++ } ++ } + + if (UNEXPECTED(ZSTR_LEN(unquoted) + 2 > ZSTR_MAX_LEN - qcount)) { + return NULL; +@@ -686,15 +705,14 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un + *c++ = '\''; + + /* foreach (chunk that ends in a quote) */ +- for (l = ZSTR_VAL(unquoted); (r = strchr(l,'\'')); l = r+1) { +- strncpy(c, l, r-l+1); +- c += (r-l+1); +- /* add the second quote */ +- *c++ = '\''; ++ for (l = ZSTR_VAL(unquoted); l < end; l++) { ++ *c++ = *l; ++ if (*l == '\'') { ++ /* add the second quote */ ++ *c++ = '\''; ++ } + } + +- /* copy the remainder */ +- strncpy(c, l, quotedlen-(c-ZSTR_VAL(quoted_str))-1); + ZSTR_VAL(quoted_str)[quotedlen-1] = '\''; + ZSTR_VAL(quoted_str)[quotedlen] = '\0'; + +@@ -787,6 +805,7 @@ static int firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sql, + { + pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data; + char *new_sql; ++ size_t new_sql_len; + + /* Firebird allows SQL statements up to 64k, so bail if it doesn't fit */ + if (ZSTR_LEN(sql) > 65536) { +@@ -814,14 +833,14 @@ static int firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sql, + we need to replace :foo by ?, and store the name we just replaced */ + new_sql = emalloc(ZSTR_LEN(sql)+1); + new_sql[0] = '\0'; +- if (!preprocess(sql, new_sql, named_params)) { ++ if (!preprocess(sql, new_sql, &new_sql_len, named_params)) { + strcpy(dbh->error_code, "07000"); + efree(new_sql); + return 0; + } + + /* prepare the statement */ +- if (isc_dsql_prepare(H->isc_status, &H->tr, s, 0, new_sql, H->sql_dialect, out_sqlda)) { ++ if (isc_dsql_prepare(H->isc_status, &H->tr, s, new_sql_len, new_sql, H->sql_dialect, out_sqlda)) { + RECORD_ERROR(dbh); + efree(new_sql); + return 0; +diff --git a/ext/pdo_firebird/tests/ghsa-w476-322c-wpvm.phpt b/ext/pdo_firebird/tests/ghsa-w476-322c-wpvm.phpt +new file mode 100644 +index 00000000000..41c1125e9b9 +--- /dev/null ++++ b/ext/pdo_firebird/tests/ghsa-w476-322c-wpvm.phpt +@@ -0,0 +1,44 @@ ++--TEST-- ++GHSA-w476-322c-wpvm: SQL injection in pdo_firebird via NUL bytes in quoted strings ++--EXTENSIONS-- ++pdo_firebird ++--SKIPIF-- ++ ++--XLEAK-- ++A bug in firebird causes a memory leak when calling `isc_attach_database()`. ++See https://github.com/FirebirdSQL/firebird/issues/7849 ++--FILE-- ++exec('CREATE TABLE ghsa_w476_322c_wpvm (name VARCHAR(255))'); ++ ++$param = $dbh->quote("\0"); ++$param2 = $dbh->quote('or 1=1--'); ++var_export($param); ++echo("\n"); ++ ++echo "prepare: "; ++$stmt = $dbh->prepare("SELECT * FROM ghsa_w476_322c_wpvm WHERE name = {$param} AND name = {$param2}"); ++$stmt->execute(); ++echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC)) . "\n"; ++ ++echo "query: "; ++$stmt = $dbh->query("SELECT * FROM ghsa_w476_322c_wpvm WHERE name = {$param} AND name = {$param2}"); ++echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC)) . "\n"; ++ ++echo "exec: "; ++$affectedRows = $dbh->exec("UPDATE ghsa_w476_322c_wpvm SET name = 'updated' WHERE name = {$param} AND name = {$param2}"); ++echo $affectedRows . "\n"; ++?> ++--CLEAN-- ++exec("DROP TABLE ghsa_w476_322c_wpvm"); ++?> ++--EXPECT-- ++'\'' . "\0" . '\'' ++prepare: [] ++query: [] ++exec: 0 +-- +2.54.0 + diff --git a/php-cve-2026-14179.patch b/php-cve-2026-14179.patch deleted file mode 100644 index 71e15cd..0000000 --- a/php-cve-2026-14179.patch +++ /dev/null @@ -1,274 +0,0 @@ -From 4b0dd469bbba7bf5f25f1a4f694aeb15c3515be4 Mon Sep 17 00:00:00 2001 -From: Saki Takamachi -Date: Sun, 3 May 2026 19:56:30 +0200 -Subject: [PATCH 07/10] GHSA-w476-322c-wpvm: [pdo_firebird] Fix SQL injection - via NUL bytes in quoted strings - -Fixes GHSA-w476-322c-wpvm -Fixes CVE-2025-14179 - -(cherry picked from commit 3f40b65323dd1b85e9bab6878237d3867e449d5c) ---- - ext/pdo_firebird/firebird_driver.c | 69 ++++++++++++------- - .../tests/ghsa-w476-322c-wpvm.phpt | 44 ++++++++++++ - 2 files changed, 88 insertions(+), 25 deletions(-) - create mode 100644 ext/pdo_firebird/tests/ghsa-w476-322c-wpvm.phpt - -diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c -index a446622c90e..25882919f86 100644 ---- a/ext/pdo_firebird/firebird_driver.c -+++ b/ext/pdo_firebird/firebird_driver.c -@@ -291,7 +291,7 @@ static FbTokenType getToken(const char** begin, const char* end) - return ret; - } - --int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) -+int preprocess(const zend_string* sql, char* sql_out, size_t* sql_out_len, HashTable* named_params) - { - bool passAsIs = 1, execBlock = 0; - zend_long pindex = -1; -@@ -322,7 +322,7 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) - if (l > 252) { - return 0; - } -- strncpy(ident, i, l); -+ memcpy(ident, i, l); - ident[l] = '\0'; - if (!strcasecmp(ident, "EXECUTE")) - { -@@ -347,7 +347,7 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) - if (l > 252) { - return 0; - } -- strncpy(ident2, i2, l); -+ memcpy(ident2, i2, l); - ident2[l] = '\0'; - execBlock = !strcasecmp(ident2, "BLOCK"); - passAsIs = 0; -@@ -363,11 +363,15 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) - - if (passAsIs) - { -- strcpy(sql_out, ZSTR_VAL(sql)); -+ memcpy(sql_out, ZSTR_VAL(sql), ZSTR_LEN(sql)); -+ sql_out[ZSTR_LEN(sql)] = '\0'; -+ *sql_out_len = ZSTR_LEN(sql); - return 1; - } - -- strncat(sql_out, start, p - start); -+ char *sql_out_p = sql_out; -+ memcpy(sql_out_p, start, p - start); -+ sql_out_p += p - start; - - while (p < end) - { -@@ -375,10 +379,12 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) - tok = getToken(&p, end); - switch (tok) - { -- case ttParamMark: -- tok = getToken(&p, end); -+ case ttParamMark: { -+ const char* p_peek = p; -+ tok = getToken(&p_peek, end); - if (tok == ttIdent /*|| tok == ttString*/) - { -+ p = p_peek; - ++pindex; - l = p - start; - /* check the length of the identifier */ -@@ -387,7 +393,7 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) - if (l > 253) { - return 0; - } -- strncpy(pname, start, l); -+ memcpy(pname, start, l); - pname[l] = '\0'; - - if (named_params) { -@@ -396,7 +402,7 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) - zend_hash_str_update(named_params, pname, l, &tmp); - } - -- strcat(sql_out, "?"); -+ *sql_out_p++ = '?'; - } - else - { -@@ -406,10 +412,11 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) - return 0; - } - ++pindex; -- strncat(sql_out, start, p - start); -+ memcpy(sql_out_p, start, p - start); -+ sql_out_p += p - start; - } - break; -- -+ } - case ttIdent: - if (execBlock) - { -@@ -421,11 +428,14 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) - if (l > 252) { - return 0; - } -- strncpy(ident, start, l); -+ memcpy(ident, start, l); - ident[l] = '\0'; - if (!strcasecmp(ident, "AS")) - { -- strncat(sql_out, start, end - start); -+ memcpy(sql_out_p, start, end - start); -+ sql_out_p += end - start; -+ *sql_out_p = '\0'; -+ *sql_out_len = sql_out_p - sql_out; - return 1; - } - } -@@ -436,7 +446,8 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) - case ttComment: - case ttString: - case ttOther: -- strncat(sql_out, start, p - start); -+ memcpy(sql_out_p, start, p - start); -+ sql_out_p += p - start; - break; - - case ttBrokenComment: -@@ -454,6 +465,8 @@ int preprocess(const zend_string* sql, char* sql_out, HashTable* named_params) - break; - } - } -+ *sql_out_p = '\0'; -+ *sql_out_len = sql_out_p - sql_out; - return 1; - } - -@@ -663,7 +676,7 @@ free_statement: - static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype) - { - size_t qcount = 0; -- char const *co, *l, *r; -+ char const *co, *l; - char *c; - size_t quotedlen; - zend_string *quoted_str; -@@ -672,9 +685,15 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un - return zend_string_init("''", 2, 0); - } - -+ const char * const end = ZSTR_VAL(unquoted) + ZSTR_LEN(unquoted); -+ - /* Firebird only requires single quotes to be doubled if string lengths are used */ - /* count the number of ' characters */ -- for (co = ZSTR_VAL(unquoted); (co = strchr(co,'\'')); qcount++, co++); -+ for (co = ZSTR_VAL(unquoted); co < end; co++) { -+ if (*co == '\'') { -+ qcount++; -+ } -+ } - - if (UNEXPECTED(ZSTR_LEN(unquoted) + 2 > ZSTR_MAX_LEN - qcount)) { - return NULL; -@@ -686,15 +705,14 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un - *c++ = '\''; - - /* foreach (chunk that ends in a quote) */ -- for (l = ZSTR_VAL(unquoted); (r = strchr(l,'\'')); l = r+1) { -- strncpy(c, l, r-l+1); -- c += (r-l+1); -- /* add the second quote */ -- *c++ = '\''; -+ for (l = ZSTR_VAL(unquoted); l < end; l++) { -+ *c++ = *l; -+ if (*l == '\'') { -+ /* add the second quote */ -+ *c++ = '\''; -+ } - } - -- /* copy the remainder */ -- strncpy(c, l, quotedlen-(c-ZSTR_VAL(quoted_str))-1); - ZSTR_VAL(quoted_str)[quotedlen-1] = '\''; - ZSTR_VAL(quoted_str)[quotedlen] = '\0'; - -@@ -787,6 +805,7 @@ static int firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sql, - { - pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data; - char *new_sql; -+ size_t new_sql_len; - - /* Firebird allows SQL statements up to 64k, so bail if it doesn't fit */ - if (ZSTR_LEN(sql) > 65536) { -@@ -814,14 +833,14 @@ static int firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sql, - we need to replace :foo by ?, and store the name we just replaced */ - new_sql = emalloc(ZSTR_LEN(sql)+1); - new_sql[0] = '\0'; -- if (!preprocess(sql, new_sql, named_params)) { -+ if (!preprocess(sql, new_sql, &new_sql_len, named_params)) { - strcpy(dbh->error_code, "07000"); - efree(new_sql); - return 0; - } - - /* prepare the statement */ -- if (isc_dsql_prepare(H->isc_status, &H->tr, s, 0, new_sql, H->sql_dialect, out_sqlda)) { -+ if (isc_dsql_prepare(H->isc_status, &H->tr, s, new_sql_len, new_sql, H->sql_dialect, out_sqlda)) { - RECORD_ERROR(dbh); - efree(new_sql); - return 0; -diff --git a/ext/pdo_firebird/tests/ghsa-w476-322c-wpvm.phpt b/ext/pdo_firebird/tests/ghsa-w476-322c-wpvm.phpt -new file mode 100644 -index 00000000000..41c1125e9b9 ---- /dev/null -+++ b/ext/pdo_firebird/tests/ghsa-w476-322c-wpvm.phpt -@@ -0,0 +1,44 @@ -+--TEST-- -+GHSA-w476-322c-wpvm: SQL injection in pdo_firebird via NUL bytes in quoted strings -+--EXTENSIONS-- -+pdo_firebird -+--SKIPIF-- -+ -+--XLEAK-- -+A bug in firebird causes a memory leak when calling `isc_attach_database()`. -+See https://github.com/FirebirdSQL/firebird/issues/7849 -+--FILE-- -+exec('CREATE TABLE ghsa_w476_322c_wpvm (name VARCHAR(255))'); -+ -+$param = $dbh->quote("\0"); -+$param2 = $dbh->quote('or 1=1--'); -+var_export($param); -+echo("\n"); -+ -+echo "prepare: "; -+$stmt = $dbh->prepare("SELECT * FROM ghsa_w476_322c_wpvm WHERE name = {$param} AND name = {$param2}"); -+$stmt->execute(); -+echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC)) . "\n"; -+ -+echo "query: "; -+$stmt = $dbh->query("SELECT * FROM ghsa_w476_322c_wpvm WHERE name = {$param} AND name = {$param2}"); -+echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC)) . "\n"; -+ -+echo "exec: "; -+$affectedRows = $dbh->exec("UPDATE ghsa_w476_322c_wpvm SET name = 'updated' WHERE name = {$param} AND name = {$param2}"); -+echo $affectedRows . "\n"; -+?> -+--CLEAN-- -+exec("DROP TABLE ghsa_w476_322c_wpvm"); -+?> -+--EXPECT-- -+'\'' . "\0" . '\'' -+prepare: [] -+query: [] -+exec: 0 --- -2.54.0 - diff --git a/php.spec b/php.spec index 347dd2b..47ad4dc 100644 --- a/php.spec +++ b/php.spec @@ -198,7 +198,7 @@ Patch91: php-7.2.0-oci8conf.patch # Upstream fixes (100+) # Security fixes (200+) -Patch200: php-cve-2026-14179.patch +Patch200: php-cve-2025-14179.patch Patch201: php-cve-2026-6722.patch Patch202: php-cve-2026-7261.patch Patch203: php-cve-2026-7262.patch -- cgit