Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions doc/source/history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ FLINT version history
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


FLINT 3.6.0 (in development)
-------------------------------------------------------------------------------

Bug fixes

* Fix ``flint_sprintf`` producing truncated output (e.g. ``"x"`` instead of
``"x1"``) on 32-bit glibc, which broke ``mpoly_test_irreducible`` and the
``compose_mpoly`` tests on i386/armhf
[EC, `#2648 <https://github.com/flintlib/flint/pull/2648>`_].


2026-04-24 -- FLINT 3.5.0
-------------------------------------------------------------------------------

Expand Down
17 changes: 0 additions & 17 deletions src/generic_files/io_vsnprintf.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,20 +163,3 @@ int flint_snprintf(char * s, size_t n, const char * str, ...)

return ret;
}

int flint_vsprintf(char * s, const char * str, va_list vlist)
{
return flint_vsnprintf(s, INT_MAX, str, vlist);
}

int flint_sprintf(char * s, const char * str, ...)
{
va_list vlist;
int ret;

va_start(vlist, str);
ret = flint_vsprintf(s, str, vlist);
va_end(vlist);

return ret;
}
136 changes: 136 additions & 0 deletions src/generic_files/io_vsprintf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
Copyright (C) 2026 Lars Göttgens
Copyright (C) 2026 Edgar Costa

This file is part of FLINT.

FLINT is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 3 of the License, or
(at your option) any later version. See <https://www.gnu.org/licenses/>.
*/

#include <ctype.h> /* isdigit */
#include <stdint.h> /* intmax_t */
#include <stdio.h>
#include <string.h> /* memcpy, strncmp and strchr */
#include <stdarg.h>
#include <wchar.h> /* wchar_t and wint_t */
#include "nmod_types.h"
#include "fmpz.h"
#include "fmpz_mod.h"
#include "fmpq_types.h"
#include "fmpq.h"
#include "arf_types.h"
#include "arb.h"
#include "acb.h"
#include "gr.h"
#include "gr_vec.h"
#include "gr_poly.h"
#include "gr_ore_poly.h"
#include "gr_mat.h"
#include "gr_mat/impl.h"

/*
Sink for flint_sprintf-style functions: writes into a caller-supplied
buffer with no length bound. Uses system vsprintf rather than vsnprintf
because some 32-bit glibc versions corrupt output when vsnprintf is
called with a very large size_t (issue #2646).
*/
typedef struct
{
char * buf;
size_t used;
} flint_vsprintf_out;

static void flint_vsprintf_init(flint_vsprintf_out * out, char * buf)
{
out->buf = buf;
out->used = 0;
out->buf[0] = '\0';
}

static int flint_vsprintf_vprintf(flint_vsprintf_out * out, const char * fmt, va_list ap)
{
va_list ap_copy;
int res;

va_copy(ap_copy, ap);
res = vsprintf(out->buf + out->used, fmt, ap_copy);
va_end(ap_copy);

if (res > 0)
out->used += (size_t) res;

return res;
}

static int flint_vsprintf_printf(flint_vsprintf_out * out, const char * fmt, ...)
{
va_list ap;
int res;

va_start(ap, fmt);
res = flint_vsprintf_vprintf(out, fmt, ap);
va_end(ap);

return res;
}

static size_t flint_vsprintf_write(const void * buf, size_t len, flint_vsprintf_out * out)
{
memcpy(out->buf + out->used, buf, len);
out->used += len;
out->buf[out->used] = '\0';
return len;
}

static int flint_vsprintf_putc(int ch, flint_vsprintf_out * out)
{
out->buf[out->used] = (char) ch;
out->used++;
out->buf[out->used] = '\0';
return (unsigned char) ch;
}

#define FLINT_VPRINTF_FUNCTION flint_vsprintf
#define FLINT_VPRINTF_FUNCTION_ARGS char * s
#define FLINT_VPRINTF_OUT_T flint_vsprintf_out
#define FLINT_VPRINTF_INIT(out) flint_vsprintf_init((out), s)
#define FLINT_VPRINTF_PRINTF(out, ...) flint_vsprintf_printf((out), __VA_ARGS__)
#define FLINT_VPRINTF_VPRINTF(out, fmt, ap) flint_vsprintf_vprintf((out), (fmt), (ap))
#define FLINT_VPRINTF_WRITE(buf, len, out) flint_vsprintf_write((buf), (len), (out))
#define FLINT_VPRINTF_PUTC(ch, out) flint_vsprintf_putc((ch), (out))
#define FLINT_VPRINTF_PUTC_ERRVAL (-1)
#define FLINT_VPRINTF_GR_STREAM_INIT(gr_out, out) gr_stream_init_str((gr_out))
#define FLINT_VPRINTF_GR_STREAM_FLUSH(gr_out, out) \
do { \
FLINT_VPRINTF_WRITE((gr_out)->s, (gr_out)->len, (out)); \
flint_free((gr_out)->s); \
} while (0)

#include "io_vprintf_impl.h"

#undef FLINT_VPRINTF_FUNCTION
#undef FLINT_VPRINTF_FUNCTION_ARGS
#undef FLINT_VPRINTF_OUT_T
#undef FLINT_VPRINTF_INIT
#undef FLINT_VPRINTF_PRINTF
#undef FLINT_VPRINTF_VPRINTF
#undef FLINT_VPRINTF_WRITE
#undef FLINT_VPRINTF_PUTC
#undef FLINT_VPRINTF_PUTC_ERRVAL
#undef FLINT_VPRINTF_GR_STREAM_INIT
#undef FLINT_VPRINTF_GR_STREAM_FLUSH

int flint_sprintf(char * s, const char * str, ...)
{
va_list vlist;
int ret;

va_start(vlist, str);
ret = flint_vsprintf(s, str, vlist);
va_end(vlist);

return ret;
}
1 change: 1 addition & 0 deletions src/test/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ test_struct tests[] =
TEST_FUNCTION(flint_ctz),
TEST_FUNCTION(flint_fprintf),
TEST_FUNCTION(flint_snprintf),
TEST_FUNCTION(flint_sprintf),
TEST_FUNCTION(flint_printf),
TEST_FUNCTION(memory_manager),
TEST_FUNCTION(sdiv_qrnnd),
Expand Down
52 changes: 52 additions & 0 deletions src/test/t-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1435,6 +1435,58 @@ TEST_FUNCTION_START(flint_snprintf, state)
TEST_FUNCTION_END(state);
}

TEST_FUNCTION_START(flint_sprintf, state)
{
/* Regression test for #2646: on 32-bit glibc, vsnprintf called with a
very large size_t (such as INT_MAX) silently drops output past the
first character, causing flint_sprintf("x%wd", n) to produce "x"
instead of "x<n>". */
char buf[64];
int got;
slong i;

for (i = 0; i < 10; i++)
{
char expected[16];
memset(buf, 'Z', sizeof(buf));
got = flint_sprintf(buf, "x%wd", i + 1);
snprintf(expected, sizeof(expected), "x%lld", (long long) (i + 1));
if (got < 0)
TEST_FUNCTION_FAIL(
"Negative return value from flint_sprintf for i=%wd.\n", i);
if (strcmp(buf, expected) != 0)
TEST_FUNCTION_FAIL(
"flint_sprintf(\"x%%wd\", %wd) gave \"%s\" expected \"%s\"\n",
i + 1, buf, expected);
}

{
slong values[] = {0, 1, -1, 12345, -12345, WORD_MIN, WORD_MAX};
size_t ix;
for (ix = 0; ix < sizeof(values) / sizeof(values[0]); ix++)
{
char expected[64];
memset(buf, 'Z', sizeof(buf));
got = flint_sprintf(buf, "[%wd]", values[ix]);
snprintf(expected, sizeof(expected), "[%lld]", (long long) values[ix]);
if (got < 0 || strcmp(buf, expected) != 0)
TEST_FUNCTION_FAIL(
"flint_sprintf(\"[%%wd]\", %wd) gave \"%s\" expected \"%s\"\n",
values[ix], buf, expected);
}
}

{
memset(buf, 'Z', sizeof(buf));
got = flint_sprintf(buf, "a%wd b%wu c%wx", (slong) -7, (ulong) 42, (ulong) 0xab);
if (got < 0 || strcmp(buf, "a-7 b42 cab") != 0)
TEST_FUNCTION_FAIL(
"flint_sprintf with multiple %%w specifiers: got \"%s\"\n", buf);
}

TEST_FUNCTION_END(state);
}


#if HAVE_UNISTD_H && FLINT_COVERAGE
#include <unistd.h>
Expand Down
Loading