From: arne@vajhoej.dk
On 6/23/2025 6:33 PM, Craig A. Berry wrote:
> I haven't had time yet to dig into why the replacement wasn't working.
I did a little digging.
tds_vasprintf contains:
// fp = NLA0:
len = vfprintf(fp, fmt, ap);
// buf = malloc(len + 1)
if (vsprintf(buf, fmt, ap) != len)
return -1;
And the test fails.
And it can be recreated outside of FreeTDS.
$ type zzzz.c
#include
#include
void test(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
int len_1 = vfprintf(stdout, fmt, ap);
// va_end(ap);
fprintf(stdout, " [len=%d]\n", len_1);
char buf[1000];
// va_start(ap, fmt);
int len_2 = vsprintf(buf, fmt, ap);
va_end(ap);
printf("%s [len=%d]\n", buf,len_2);
}
int main(int argc, char *argv[])
{
test("INSERT INTO t1 VALUES(%d, '%s')", 999, "XXX");
test("INSERT INTO t1 VALUES(%d, '%s')", 999, "XXX");
test("INSERT INTO t1 VALUES(%d, '%s')", 999, "XXX");
return 0;
}
$ cc zzzz
$ link zzzz
$ r zzzz
INSERT INTO t1 VALUES(999, 'XXX') [len=33]
INSERT INTO t1 VALUES(2060040508, '') [len=37]
INSERT INTO t1 VALUES(999, 'XXX') [len=33]
INSERT INTO t1 VALUES(2068038401, '') [len=37]
INSERT INTO t1 VALUES(999, 'XXX') [len=33]
INSERT INTO t1 VALUES(2068038401, '') [len=37]
It seems like vfprintf mess up ap so it is no longer good
for vsprintf.
And that is supposedly perfectly legal. C99 says:
As the functions vfprintf, vfscanf, vprintf, vscanf, vsnprintf,
vsprintf, and
vsscanf invoke the va_arg macro, the value of arg after the return is
indeterminate.
Unfortunately that means that the entire fallback algorithm
in tds_vasprintf (write to null device to find output size,
allocate buffer of correct size and then write to buffer)
is broken.
Arne
--- SoupGate-Win32 v1.05
* Origin: you cannot sedate... all the things you hate (1:229/2)
|