Dear Maintainer,
I am the maintainer of the `odbc-sys` Rust crate. After updating the
runners of the GitHub workflow to Ubuntu 26.04 my tests against iodbc
started failing. I told Claude to investigate the issue and it found
that the package should be build with -std=gnu17 rather than -std=c17.
Underneath the detailed root cause analysis as generated by claude
---
Since 3.52.16-1, every program linked against libiodbc2 segfaults on its
first ODBC call, allocating an environment handle with SQLAllocHandle. I
reproduced this on Ubuntu 26.04 ("resolute"), which ships 3.52.16-2
unchanged from Debian. 3.52.9-4 (Ubuntu 24.04) works fine with the same
program.
Cause
-----
3.52.16-1 started building with -std=c17 (#1097202). That is strict ISO C,
so glibc's <string.h> does not declare POSIX functions such as strdup()
and strcasecmp(). debian/rules also passes
-Wno-implicit-function-declaration -Wno-int-conversion
-Wno-incompatible-pointer-types
which hides the resulting diagnostics. Without these flags the build fails:
../include/iodbc.h:116:34: error: implicit declaration of function
'strdup' [-Wimplicit-function-declaration]
SQLGetInstalledDrivers.c:104:10: error: implicit declaration of
function 'strcasecmp' [-Wimplicit-function-declaration]
SQLGetInstalledDrivers.c:167:31: error: assignment to 'void *' from
'int' makes pointer from integer without a cast [-Wint-conversion]
Because strdup() is implicitly declared as returning int, the returned
64-bit pointer is truncated to 32 bits. In _iodbcdm_cfg_init()
(iodbcinst/inifile.c), pconfig->fileName = STRDUP(filename) stores such
a truncated pointer, and _iodbcdm_cfg_done() then calls free() on it.
This happens while the environment handle is allocated, when the driver
manager reads "AppUnicodeType" from odbcinst.ini, so every application
crashes.
Backtrace from a -g -O0 build using the flags from debian/rules (note the
32-bit value passed to free()):
Program received signal SIGSEGV, Segmentation fault.
#0 __GI___libc_free (mem=0x6f63e0e0) at ./malloc/malloc.c:3333
#1 _iodbcdm_cfg_done (pconfig=0x5fe96f63e070) at inifile.c:182
#2 _iodbcdm_cfg_init (ppconf=..., filename="/etc/odbcinst.ini",
doCreate=0) at inifile.c:163
#3 _iodbcdm_cfg_search_init (ppconf=..., filename="odbcinst.ini",
doCreate=0) at inifile.c:1262
#4 GetPrivateProfileString (lpszSection="ODBC",
lpszEntry="AppUnicodeType", ...) at SQLGetPrivateProfileString.c:98
Reproducer
----------
$ cat t.c
#include <stdio.h>
#include <sql.h>
#include <sqlext.h>
int main(void) {
SQLHANDLE env, dbc;
SQLRETURN r;
r = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
printf("env %d\n", r);
r = SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER)SQL_OV_ODBC3, 0);
r = SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc); printf("dbc %d\n", r);
SQLFreeHandle(SQL_HANDLE_DBC, dbc);
SQLFreeHandle(SQL_HANDLE_ENV, env);
puts("ok");
return 0;
}
$ gcc t.c -I/usr/include/iodbc -liodbc -o t && ./t
Segmentation fault (core dumped)
No odbc.ini or odbcinst.ini needs to exist.
Fix
---
Build with -std=gnu17 instead of -std=c17. This still avoids the C23
issues from #1097202 (the old-style "()" function pointer declarations),
but keeps the POSIX/GNU declarations visible. With -std=gnu17 the
upstream sources compiled cleanly with GCC 15.2.0 even without the three
-Wno-* flags, and the reproducer above runs successfully (env 0, dbc 0,
ok). I would suggest dropping those flags as well, so that problems like
this one fail the build again:
--- a/debian/rules
+++ b/debian/rules
@@
-export CFLAGS += -std=c17 -Wno-implicit-function-declaration
-Wno-int-conversion -Wno-incompatible-pointer-types
+export CFLAGS += -std=gnu17
I tested this with the upstream configure/make on the 3.52.16-2 source,
not with a full dpkg-buildpackage run.
Environment where it was reproduced: Ubuntu 26.04 (Docker image
ubuntu:26.04), libiodbc2 3.52.16-2, gcc 15.2.0, x86_64.
Found via the CI of the Rust crate odbc-sys:
https://github.com/pacman82/odbc-sys/actions/runs/41037444708/job/124001751617
Regards,
Markus Klein