[php-src] PHP-8.4: Fix GH-20726: crash in pdo_odbc with pooling and no credentials (#22512)

Ilia Alshanetsky via GitHub <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Ilia Alshanetsky (iliaal)
Committer: GitHub (web-flow)
Pusher: NattyNarwhal
Date: 2026-06-30T02:23:44-03:00

Commit: https://github.com/php/php-src/commit/c8d90e7e18947084c003b0645718f8a40af94c58
Raw diff: https://github.com/php/php-src/commit/c8d90e7e18947084c003b0645718f8a40af94c58.diff

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.

Changed paths:
  M  NEWS
  M  ext/pdo_odbc/odbc_driver.c


Diff:

diff --git a/NEWS b/NEWS
index 57d91e345457..d03a77b01d91 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 1ddce74f3f92..2cdac30223c0 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) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.