Commit c8d90e7e189 for php.net

commit c8d90e7e18947084c003b0645718f8a40af94c58
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Tue Jun 30 01:23:44 2026 -0400

    Fix GH-20726: crash in pdo_odbc with pooling and no credentials (#22512)

    When a DSN carries no credentials, dbh->username and dbh->password are
    NULL. With ODBC connection pooling enabled, the unixODBC driver manager
    compares the cached and requested credentials with strcmp() while
    matching a pooled connection, dereferencing the NULL pointers and
    crashing inside SQLConnect. Pass empty strings instead. This hardens the
    connect path; the pooling use-after-free in GH-20726 is a separate
    unixODBC defect.

diff --git a/NEWS b/NEWS
index 57d91e34545..d03a77b01d9 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,10 @@ PHP                                                                        NEWS
   . Fixed IntlChar methods leaving stale global error state after successful
     calls. (Xuyang Zhang)

+- PDO_ODBC:
+  . Fixed bug GH-20726 (Crash with ODBC connection pooling when the DSN
+    carries no credentials). (iliaal)
+
 - Phar:
   . Fixed inconsistent handling of the magic ".phar" directory. Paths such as
     "/.phar" remain protected, while non-magic paths that merely start with
diff --git a/ext/pdo_odbc/odbc_driver.c b/ext/pdo_odbc/odbc_driver.c
index 1ddce74f3f9..2cdac30223c 100644
--- a/ext/pdo_odbc/odbc_driver.c
+++ b/ext/pdo_odbc/odbc_driver.c
@@ -601,7 +601,11 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{
 				dsnbuf, sizeof(dsnbuf)-1, &dsnbuflen, SQL_DRIVER_NOPROMPT);
 	}
 	if (!use_direct) {
-		rc = SQLConnect(H->dbc, (SQLCHAR *) dbh->data_source, SQL_NTS, (SQLCHAR *) dbh->username, SQL_NTS, (SQLCHAR *) dbh->password, SQL_NTS);
+		/* unixODBC pooling strcmp()s the credentials when matching a cached
+		 * connection and crashes on a NULL username/password, so pass "". */
+		rc = SQLConnect(H->dbc, (SQLCHAR *) dbh->data_source, SQL_NTS,
+			(SQLCHAR *) (dbh->username ? dbh->username : ""), SQL_NTS,
+			(SQLCHAR *) (dbh->password ? dbh->password : ""), SQL_NTS);
 	}

 	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {