Message-ID: From: "jarvis24young (@jarvis24young)" To: "postgresql-interfaces/psqlodbc" Date: Sat, 25 Apr 2026 04:01:49 +0000 Subject: [postgresql-interfaces/psqlodbc] issue #177: OOM: SQLAllocHandle(SQL_HANDLE_STMT) can segfault if ARD_AllocBookmark malloc fails List-Id: X-GitHub-Author-Id: 48787405 X-GitHub-Author-Login: jarvis24young X-GitHub-Issue: 177 X-GitHub-Repo: postgresql-interfaces/psqlodbc X-GitHub-State: open X-GitHub-Type: issue X-GitHub-Url: https://github.com/postgresql-interfaces/psqlodbc/issues/177 Content-Type: text/plain; charset=utf-8 # OOM: SQLAllocHandle(SQL_HANDLE_STMT) can segfault if ARD_AllocBookmark malloc fails ## Summary While doing defensive robustness testing of psqlODBC, I found a NULL-dereference crash on the statement allocation path when an internal bookmark allocation fails. This is an OOM/robustness issue. I am not claiming a practical remote exploit; the reproducer uses malloc-failure injection to force the allocation failure deterministically. The expected behavior would be to return an ODBC error/diagnostic instead of crashing the hosting process. ## Affected path `SQLAllocHandle(SQL_HANDLE_STMT, ...)` reaches `PGAPI_AllocStmt()`, which calls `ARD_AllocBookmark()`: ```c BindInfoClass * ARD_AllocBookmark(ARDFields *ardopts) { if (!ardopts->bookmark) { ardopts->bookmark = (BindInfoClass *) malloc(sizeof(BindInfoClass)); pg_memset(ardopts->bookmark, 0, sizeof(BindInfoClass)); } return ardopts->bookmark; } ``` If `malloc(sizeof(BindInfoClass))` returns `NULL`, `pg_memset()` is called with a NULL pointer. Observed call chain: ```text SQLAllocHandle(SQL_HANDLE_STMT) -> PGAPI_AllocStmt statement.c:232 -> ARD_AllocBookmark descriptor.c:345 -> malloc(sizeof(BindInfoClass)) returns NULL -> pg_memset(NULL, ...) ``` On my x86_64 Linux build, `sizeof(BindInfoClass)` was 40 bytes. ## Reproducer Environment used: - Linux under WSL2 - unixODBC - PostgreSQL test database - psqlODBC built with debug/gcov flags, no ASan for this OOM injection run A minimal black-box test only uses public ODBC APIs: ```c SQLDriverConnect(...); SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt); ``` I forced the relevant allocation to fail with an `LD_PRELOAD` malloc shim that returns `NULL` on the Nth 40-byte allocation: ```bash PODBC_FAIL_MALLOC_SIZE=40 PODBC_FAIL_MALLOC_N=178 \ LD_PRELOAD=./exe/malloc_fail_shim.so \ ODBCSYSINI=. ODBCINSTINI=./odbcinst.ini ODBCINI=./odbc.ini \ ./exe/ard-bookmark-oom-bugwatch-test ``` Observed output: ```text connected allocating statement handle through SQLAllocHandle(SQL_HANDLE_STMT) malloc-fail-shim: failing malloc(40) occurrence 178 ../.libs/psqlodbcw.so(+0x8f5e9) ../.libs/psqlodbcw.so(+0x7cea0) ../.libs/psqlodbcw.so(SQLAllocHandle+0x20e) oom_rc=139 ``` Address mapping: ```text driver +0x8f5e9: ARD_AllocBookmark .../descriptor.c:345 driver +0x7cea0: PGAPI_AllocStmt .../statement.c:232 ``` Normal run without malloc failure completes successfully: ```text connected allocating statement handle through SQLAllocHandle(SQL_HANDLE_STMT) SQLAllocHandle stmt rc=0 hstmt=... disconnecting statement allocation completed without crashing ``` ## Expected behavior If bookmark allocation fails during statement allocation, the driver should return an ODBC error, ideally with a memory-allocation diagnostic, rather than dereferencing NULL and crashing. ## Suggested fix direction `ARD_AllocBookmark()` could return `NULL` when allocation fails, and callers that require the bookmark should handle that by setting an appropriate diagnostic and returning `SQL_ERROR`. Known direct callers worth checking: - `statement.c::PGAPI_AllocStmt()` - `bind.c::PGAPI_BindCol()` - `descriptor.c::ARDFields_copy()` I can provide the regression test and malloc-failure shim if that would be useful.