diff --git a/meta-oe/recipes-dbs/psqlodbc/files/CVE-2026-109448.patch b/meta-oe/recipes-dbs/psqlodbc/files/CVE-2026-109448.patch new file mode 100644 index 0000000000..12f6cdd1d8 --- /dev/null +++ b/meta-oe/recipes-dbs/psqlodbc/files/CVE-2026-109448.patch @@ -0,0 +1,243 @@ +From 5c9443fb0c8c0e263e1f8d0298987e91c4f5ccea Mon Sep 17 00:00:00 2001 +From: Dave Cramer +Date: Mon, 12 Oct 2026 11:47:08 -0400 +Subject: [PATCH] Merge commit from fork + +The APD, IPD and put-data binding counts are SQLSMALLINT. Binding +parameter 32768 wrapped the count to -32768, so execution failed with +07002 even though every parameter was bound; binding parameter 32769 +then extended the arrays with a memset starting at index -32768, +writing zeros up to 1.25 MiB before the start of the APD array (and +likewise for the IPD and put-data arrays), which corrupts the heap. + +SQLNumParams and SQL_DESC_COUNT are SQLSMALLINT too, so 32767 is the +largest parameter count ODBC can describe. PGAPI_BindParameter now +returns 07009 for parameter numbers outside 1..32767, which also covers +parameter number 0 for applications that call the driver without a +driver manager. + +New param-count-limit test binds 32767, 32768 and 32769 parameters. + +Assisted-By: Claude +(cherry picked from commit 86e4418e9553aa0891315db7531f8cd31eb1575e) + +CVE: CVE-2026-109448 +Upstream-Status: Backport [https://github.com/postgresql-interfaces/psqlodbc/commit/86e4418e9553aa0891315db7531f8cd31eb1575e] +Signed-off-by: Ankur Tyagi +--- + bind.c | 12 +++ + test/expected/param-count-limit.out | 12 +++ + test/src/param-count-limit-test.c | 142 ++++++++++++++++++++++++++++ + test/tests | 1 + + 4 files changed, 167 insertions(+) + create mode 100644 test/expected/param-count-limit.out + create mode 100644 test/src/param-count-limit-test.c + +diff --git a/bind.c b/bind.c +index 3b566ec..b3e5ca3 100644 +--- a/bind.c ++++ b/bind.c +@@ -16,6 +16,7 @@ + /* #include */ + #include + #include ++#include + #include "bind.h" + #include "misc.h" + +@@ -57,6 +58,17 @@ PGAPI_BindParameter(HSTMT hstmt, + } + SC_clear_error(stmt); + ++ /* ++ * The APD, IPD and put-data counts are SQLSMALLINT, like SQLNumParams ++ * and SQL_DESC_COUNT, so a larger parameter number would wrap the count ++ * negative and the next binding would write before the bindings array. ++ */ ++ if (ipar < 1 || ipar > SHRT_MAX) ++ { ++ SC_set_error(stmt, STMT_BAD_PARAMETER_NUMBER_ERROR, "Invalid parameter number for PGAPI_BindParameter.", func); ++ return SQL_ERROR; ++ } ++ + apdopts = SC_get_APDF(stmt); + if (apdopts->allocated < ipar) + extend_parameter_bindings(apdopts, ipar); +diff --git a/test/expected/param-count-limit.out b/test/expected/param-count-limit.out +new file mode 100644 +index 0000000..623bcbf +--- /dev/null ++++ b/test/expected/param-count-limit.out +@@ -0,0 +1,12 @@ ++connected ++APD SQL_DESC_COUNT=-1: error SQLSTATE=07009 ++APD bind 1 after SQL_DESC_COUNT=-1: ok ++IPD SQL_DESC_COUNT=-1: error SQLSTATE=07009 ++IPD bind 1 after SQL_DESC_COUNT=-1: ok ++bind 0: error SQLSTATE=07009 ++32767 params, bind 32767: ok ++32767 params, execute: ok ++32767 params, result 1 ++32768 params, bind 32768: error SQLSTATE=07009 ++32769 params, bind 32768: error SQLSTATE=07009 ++disconnecting +diff --git a/test/src/param-count-limit-test.c b/test/src/param-count-limit-test.c +new file mode 100644 +index 0000000..e9554cd +--- /dev/null ++++ b/test/src/param-count-limit-test.c +@@ -0,0 +1,142 @@ ++/* ++ * Test the limits on parameter numbers and descriptor counts. The driver ++ * keeps parameter binding counts in SQLSMALLINT, so SQLBindParameter with a ++ * parameter number above 32767, or SQL_DESC_COUNT set negative, used to ++ * wrap the count negative and make the next binding write zeros before the ++ * start of the bindings array. ++ */ ++#include ++#include ++#include ++ ++#include "common.h" ++ ++#define MAX_PARAMS 32767 ++ ++static void ++print_state(const char *what, SQLRETURN rc, SQLSMALLINT htype, SQLHANDLE h) ++{ ++ SQLCHAR state[6] = ""; ++ SQLINTEGER native; ++ SQLCHAR msg[256]; ++ SQLSMALLINT len; ++ ++ printf("%s: ", what); ++ if (SQL_SUCCEEDED(rc)) ++ { ++ printf("ok\n"); ++ return; ++ } ++ SQLGetDiagRec(htype, h, 1, state, &native, msg, sizeof(msg), &len); ++ printf("error SQLSTATE=%s\n", (char *) state); ++} ++ ++/* SELECT 1 WHERE 1 IN (?, ?, ..., ?) with nparams markers, all bound to 1 */ ++static void ++run_with_params(int nparams) ++{ ++ SQLRETURN rc; ++ HSTMT hstmt = SQL_NULL_HSTMT; ++ SQLINTEGER one = 1; ++ SQLINTEGER result = 0; ++ char *sql, *p; ++ char what[64]; ++ int i; ++ ++ rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt); ++ CHECK_CONN_RESULT(rc, "SQLAllocHandle failed", conn); ++ ++ sql = malloc(32 + 2 * nparams); ++ p = sql + sprintf(sql, "SELECT 1 WHERE 1 IN ("); ++ for (i = 0; i < nparams; i++) ++ { ++ if (i > 0) ++ *p++ = ','; ++ *p++ = '?'; ++ } ++ strcpy(p, ")"); ++ ++ for (i = 1; i <= nparams; i++) ++ { ++ rc = SQLBindParameter(hstmt, (SQLUSMALLINT) i, SQL_PARAM_INPUT, ++ SQL_C_SLONG, SQL_INTEGER, 0, 0, &one, 0, NULL); ++ if (!SQL_SUCCEEDED(rc)) ++ break; ++ } ++ snprintf(what, sizeof(what), "%d params, bind %d", nparams, i <= nparams ? i : nparams); ++ print_state(what, rc, SQL_HANDLE_STMT, hstmt); ++ ++ if (SQL_SUCCEEDED(rc)) ++ { ++ rc = SQLExecDirect(hstmt, (SQLCHAR *) sql, SQL_NTS); ++ snprintf(what, sizeof(what), "%d params, execute", nparams); ++ print_state(what, rc, SQL_HANDLE_STMT, hstmt); ++ } ++ if (SQL_SUCCEEDED(rc)) ++ { ++ rc = SQLBindCol(hstmt, 1, SQL_C_SLONG, &result, 0, NULL); ++ CHECK_STMT_RESULT(rc, "SQLBindCol failed", hstmt); ++ rc = SQLFetch(hstmt); ++ CHECK_STMT_RESULT(rc, "SQLFetch failed", hstmt); ++ printf("%d params, result %d\n", nparams, (int) result); ++ } ++ ++ free(sql); ++ SQLFreeHandle(SQL_HANDLE_STMT, hstmt); ++} ++ ++/* Set SQL_DESC_COUNT of the statement's APD or IPD to -1, then bind */ ++static void ++negative_desc_count(SQLINTEGER desc_attr, const char *desc_name) ++{ ++ SQLRETURN rc; ++ HSTMT hstmt = SQL_NULL_HSTMT; ++ SQLHDESC hdesc; ++ SQLINTEGER one = 1; ++ char what[64]; ++ ++ rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt); ++ CHECK_CONN_RESULT(rc, "SQLAllocHandle failed", conn); ++ ++ rc = SQLGetStmtAttr(hstmt, desc_attr, &hdesc, 0, NULL); ++ CHECK_STMT_RESULT(rc, "SQLGetStmtAttr failed", hstmt); ++ ++ rc = SQLSetDescField(hdesc, 0, SQL_DESC_COUNT, (SQLPOINTER) (SQLLEN) -1, 0); ++ snprintf(what, sizeof(what), "%s SQL_DESC_COUNT=-1", desc_name); ++ print_state(what, rc, SQL_HANDLE_DESC, hdesc); ++ ++ rc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_SLONG, ++ SQL_INTEGER, 0, 0, &one, 0, NULL); ++ snprintf(what, sizeof(what), "%s bind 1 after SQL_DESC_COUNT=-1", desc_name); ++ print_state(what, rc, SQL_HANDLE_STMT, hstmt); ++ ++ SQLFreeHandle(SQL_HANDLE_STMT, hstmt); ++} ++ ++int ++main(int argc, char **argv) ++{ ++ SQLRETURN rc; ++ HSTMT hstmt = SQL_NULL_HSTMT; ++ SQLINTEGER one = 1; ++ ++ test_connect(); ++ ++ negative_desc_count(SQL_ATTR_APP_PARAM_DESC, "APD"); ++ negative_desc_count(SQL_ATTR_IMP_PARAM_DESC, "IPD"); ++ ++ rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt); ++ CHECK_CONN_RESULT(rc, "SQLAllocHandle failed", conn); ++ rc = SQLBindParameter(hstmt, 0, SQL_PARAM_INPUT, SQL_C_SLONG, ++ SQL_INTEGER, 0, 0, &one, 0, NULL); ++ print_state("bind 0", rc, SQL_HANDLE_STMT, hstmt); ++ SQLFreeHandle(SQL_HANDLE_STMT, hstmt); ++ ++ run_with_params(MAX_PARAMS); ++ run_with_params(MAX_PARAMS + 1); ++ run_with_params(MAX_PARAMS + 2); ++ ++ test_disconnect(); ++ ++ return 0; ++} +diff --git a/test/tests b/test/tests +index 5074bca..c8af1ab 100644 +--- a/test/tests ++++ b/test/tests +@@ -63,6 +63,7 @@ TESTBINS = exe/connect-test \ + exe/descriptors-free-test \ + exe/primarykeys-include-test \ + exe/interval-overflow-test \ ++ exe/param-count-limit-test \ + exe/conn-settings-test \ + exe/percent-decode-test \ + exe/dbms-version-test \ diff --git a/meta-oe/recipes-dbs/psqlodbc/psqlodbc_18.00.0002.bb b/meta-oe/recipes-dbs/psqlodbc/psqlodbc_18.00.0002.bb index ae1d603e1d..9756bcfad2 100644 --- a/meta-oe/recipes-dbs/psqlodbc/psqlodbc_18.00.0002.bb +++ b/meta-oe/recipes-dbs/psqlodbc/psqlodbc_18.00.0002.bb @@ -23,6 +23,7 @@ SRC_URI = "git://github.com/postgresql-interfaces/psqlodbc.git;protocol=https;br file://psqlodbc-remove-some-checks-for-cross-compiling.patch \ file://psqlodbc-donot-use-the-hardcode-libdir.patch \ file://psqlodbc-fix-for-ptest-support.patch \ + file://CVE-2026-109448.patch \ file://run-ptest \ "