From ff708a48913156b43e27c96f4e939746b9755d70 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Mon, 27 Apr 2026 13:20:26 -0300 Subject: [PATCH 1/2] ext/odbc: get exact length for quoted connection strings strlen has to scan the string until the end of the null terminator, we might as well do the same but scan for end quotes that need escaping. --- main/php_odbc_utils.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/main/php_odbc_utils.c b/main/php_odbc_utils.c index d3b18f2bd642..f199f52187ec 100644 --- a/main/php_odbc_utils.c +++ b/main/php_odbc_utils.c @@ -75,8 +75,16 @@ PHPAPI bool php_odbc_connstr_should_quote(const char *str) */ PHPAPI size_t php_odbc_connstr_estimate_quote_length(const char *in_str) { - /* Assume all '}'. Include '{,' '}', and the null terminator too */ - return (strlen(in_str) * 2) + 3; + /* Start with including the quotes ({}) and the null terminator */ + size_t size = 3; + /* Scan the string like strlen, doubling each } character. */ + while (*in_str) { + size++; + if (*in_str++ == '}') { + size++; + } + } + return size; } /** From b64de4ecc40fcb7a0d221f4c65aeb35864d4f603 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Mon, 27 Apr 2026 13:46:19 -0300 Subject: [PATCH 2/2] ext/odbc: rename function now that it gets the exact length rather than an estimate --- ext/odbc/odbc_utils.c | 2 +- ext/odbc/php_odbc.c | 12 ++++++------ ext/pdo_odbc/odbc_driver.c | 12 ++++++------ main/php_odbc_utils.c | 4 ++-- main/php_odbc_utils.h | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ext/odbc/odbc_utils.c b/ext/odbc/odbc_utils.c index 742bf54bb292..8468fa9a06c1 100644 --- a/ext/odbc/odbc_utils.c +++ b/ext/odbc/odbc_utils.c @@ -57,7 +57,7 @@ PHP_FUNCTION(odbc_connection_string_quote) Z_PARAM_STR(str) ZEND_PARSE_PARAMETERS_END(); - size_t new_size = php_odbc_connstr_estimate_quote_length(ZSTR_VAL(str)); + size_t new_size = php_odbc_connstr_get_quoted_length(ZSTR_VAL(str)); zend_string *new_string = zend_string_alloc(new_size, 0); php_odbc_connstr_quote(ZSTR_VAL(new_string), ZSTR_VAL(str), new_size); /* reset length */ diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 95c872c293f1..1d2bd6695563 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -1951,9 +1951,9 @@ bool odbc_sqlconnect(zval *zv, char *db, char *uid, char *pwd, int cur_opt, bool if (use_uid_arg) { should_quote_uid = !php_odbc_connstr_is_quoted(uid) && php_odbc_connstr_should_quote(uid); if (should_quote_uid) { - size_t estimated_length = php_odbc_connstr_estimate_quote_length(uid); - uid_quoted = emalloc(estimated_length); - php_odbc_connstr_quote(uid_quoted, uid, estimated_length); + size_t quoted_length = php_odbc_connstr_get_quoted_length(uid); + uid_quoted = emalloc(quoted_length); + php_odbc_connstr_quote(uid_quoted, uid, quoted_length); } else { uid_quoted = uid; } @@ -1966,9 +1966,9 @@ bool odbc_sqlconnect(zval *zv, char *db, char *uid, char *pwd, int cur_opt, bool if (use_pwd_arg) { should_quote_pwd = !php_odbc_connstr_is_quoted(pwd) && php_odbc_connstr_should_quote(pwd); if (should_quote_pwd) { - size_t estimated_length = php_odbc_connstr_estimate_quote_length(pwd); - pwd_quoted = emalloc(estimated_length); - php_odbc_connstr_quote(pwd_quoted, pwd, estimated_length); + size_t quoted_length = php_odbc_connstr_get_quoted_length(pwd); + pwd_quoted = emalloc(quoted_length); + php_odbc_connstr_quote(pwd_quoted, pwd, quoted_length); } else { pwd_quoted = pwd; } diff --git a/ext/pdo_odbc/odbc_driver.c b/ext/pdo_odbc/odbc_driver.c index 4c627419d18e..3c8afe2d4215 100644 --- a/ext/pdo_odbc/odbc_driver.c +++ b/ext/pdo_odbc/odbc_driver.c @@ -548,9 +548,9 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ if (use_uid_arg) { should_quote_uid = !php_odbc_connstr_is_quoted(dbh->username) && php_odbc_connstr_should_quote(dbh->username); if (should_quote_uid) { - size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->username); - uid = emalloc(estimated_length); - php_odbc_connstr_quote(uid, dbh->username, estimated_length); + size_t quoted_length = php_odbc_connstr_get_quoted_length(dbh->username); + uid = emalloc(quoted_length); + php_odbc_connstr_quote(uid, dbh->username, quoted_length); } else { uid = dbh->username; } @@ -565,9 +565,9 @@ static int pdo_odbc_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /* {{{ if (use_pwd_arg) { should_quote_pwd = !php_odbc_connstr_is_quoted(dbh->password) && php_odbc_connstr_should_quote(dbh->password); if (should_quote_pwd) { - size_t estimated_length = php_odbc_connstr_estimate_quote_length(dbh->password); - pwd = emalloc(estimated_length); - php_odbc_connstr_quote(pwd, dbh->password, estimated_length); + size_t quoted_length = php_odbc_connstr_get_quoted_length(dbh->password); + pwd = emalloc(quoted_length); + php_odbc_connstr_quote(pwd, dbh->password, quoted_length); } else { pwd = dbh->password; } diff --git a/main/php_odbc_utils.c b/main/php_odbc_utils.c index f199f52187ec..797a4ac04be1 100644 --- a/main/php_odbc_utils.c +++ b/main/php_odbc_utils.c @@ -71,9 +71,9 @@ PHPAPI bool php_odbc_connstr_should_quote(const char *str) } /** - * Estimates the worst-case scenario for a quoted version of a string's size. + * Gets the length of a string after it has been quoted. */ -PHPAPI size_t php_odbc_connstr_estimate_quote_length(const char *in_str) +PHPAPI size_t php_odbc_connstr_get_quoted_length(const char *in_str) { /* Start with including the quotes ({}) and the null terminator */ size_t size = 3; diff --git a/main/php_odbc_utils.h b/main/php_odbc_utils.h index 78353b49a814..f2bc46ebc5ba 100644 --- a/main/php_odbc_utils.h +++ b/main/php_odbc_utils.h @@ -16,5 +16,5 @@ PHPAPI bool php_odbc_connstr_is_quoted(const char *str); PHPAPI bool php_odbc_connstr_should_quote(const char *str); -PHPAPI size_t php_odbc_connstr_estimate_quote_length(const char *in_str); +PHPAPI size_t php_odbc_connstr_get_quoted_length(const char *in_str); PHPAPI size_t php_odbc_connstr_quote(char *out_str, const char *in_str, size_t out_str_size);