From 91e180c2e20a8d899b88d516cb9d0b733c9c7693 Mon Sep 17 00:00:00 2001 From: user01010111 Date: Mon, 31 Aug 2026 12:47:25 +1200 Subject: [PATCH 1/3] common/snprintf.c: preserve wide integer values Keep fallback integer formatting at LLONG width. This prevents pointers and long-long values from being truncated through long on LLP64 systems. Handle the signed minimum without overflow and extend TEST_SNPRINTF with pointer and long-long regression comparisons. Fixes #1602 AI assistance: OpenAI Codex gpt-5.6-sol was used for repository analysis, implementation, review, drafting and validation. The human contributor reviewed the change and remains responsible for it. Signed-off-by: user01010111 --- common/snprintf.c | 78 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 15 deletions(-) diff --git a/common/snprintf.c b/common/snprintf.c index d4898b5fc4..cf4aa2634a 100644 --- a/common/snprintf.c +++ b/common/snprintf.c @@ -53,10 +53,16 @@ #include "config.h" +#include #include #include #include +#ifdef TEST_SNPRINTF +extern int printf (const char *format, ...); +extern int sprintf (char *str, const char *format, ...); +#endif + #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) /* Define this as a fall through, HAVE_STDARG_H is probably already set */ @@ -112,7 +118,7 @@ static void dopr (char *buffer, size_t maxlen, const char *format, static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, char *value, int flags, int min, int max); static void fmtint (char *buffer, size_t *currlen, size_t maxlen, - long value, int base, int min, int max, int flags); + LLONG value, int base, int min, int max, int flags); static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, int min, int max, int flags); static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); @@ -320,7 +326,7 @@ static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) else if (cflags == DP_C_LONG) value = (long)va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) - value = (long)va_arg (args, unsigned LLONG); + value = (LLONG)va_arg (args, unsigned LLONG); else value = (long)va_arg (args, unsigned int); fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); @@ -400,13 +406,8 @@ static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) break; case 'p': strvalue = va_arg (args, void *); - /* FIXME: in 64-bit (and Windows-targeted) builds this code yields: - * warning: cast from pointer to integer of different size - * so probably prints a truncated pointer value. Should check - * if sizeof(void*) == sizeof(long) and output e.g. two pieces - * (lower and upper long for bytes of the pointer). - */ - fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); + flags |= DP_F_UNSIGNED; + fmtint (buffer, &currlen, maxlen, (LLONG) strvalue, 16, min, max, flags); break; case 'n': if (cflags == DP_C_SHORT) @@ -503,11 +504,11 @@ static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ static void fmtint (char *buffer, size_t *currlen, size_t maxlen, - long value, int base, int min, int max, int flags) + LLONG value, int base, int min, int max, int flags) { int signvalue = 0; - unsigned long uvalue; - char convert[20]; + unsigned LLONG uvalue; + char convert[sizeof(value) * CHAR_BIT + 1]; int place = 0; int spadlen = 0; /* amount to space pad */ int zpadlen = 0; /* amount to zero pad */ @@ -522,7 +523,7 @@ static void fmtint (char *buffer, size_t *currlen, size_t maxlen, { if( value < 0 ) { signvalue = '-'; - uvalue = -value; + uvalue = -uvalue; } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ @@ -539,8 +540,7 @@ static void fmtint (char *buffer, size_t *currlen, size_t maxlen, (caps? "0123456789ABCDEF":"0123456789abcdef") [uvalue % (unsigned)base ]; uvalue = (uvalue / (unsigned)base ); - } while(uvalue && (place < 20)); - if (place == 20) place--; + } while(uvalue && (place < (int)(sizeof(convert) - 1))); convert[place] = 0; zpadlen = max - place; @@ -942,6 +942,12 @@ static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) NULL }; long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; + static int pointer_num; +#ifdef HAVE_LONG_LONG_INT + LLONG llong_nums[] = { ((LLONG)1 << 32) + 1, LLONG_MIN }; +#endif + char *ptr1; + char *ptr2; int x, y; int fail = 0; int num = 0; @@ -975,6 +981,48 @@ static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) } num++; } + + snprintf (buf1, sizeof (buf1), "%p", (void *)&pointer_num); + sprintf (buf2, "%p", (void *)&pointer_num); + ptr1 = buf1; + ptr2 = buf2; + if (ptr1[0] == '0' && (ptr1[1] == 'x' || ptr1[1] == 'X')) + ptr1 += 2; + if (ptr2[0] == '0' && (ptr2[1] == 'x' || ptr2[1] == 'X')) + ptr2 += 2; + while (ptr1[0] == '0' && ptr1[1] != '\0') + ptr1++; + while (ptr2[0] == '0' && ptr2[1] != '\0') + ptr2++; + while (*ptr1 && *ptr2 + && tolower ((unsigned char)*ptr1) == tolower ((unsigned char)*ptr2)) + { + ptr1++; + ptr2++; + } + if (*ptr1 || *ptr2) + { + printf("snprintf doesn't match Format: %%p\n\tsnprintf = %s\n\tsprintf = %s\n", + buf1, buf2); + fail++; + } + num++; + +#ifdef HAVE_LONG_LONG_INT + for (x = 0; x < 2; x++) + { + snprintf (buf1, sizeof (buf1), "%lld", llong_nums[x]); + sprintf (buf2, "%lld", llong_nums[x]); + if (strcmp (buf1, buf2)) + { + printf("snprintf doesn't match Format: %%lld\n\tsnprintf = %s\n\tsprintf = %s\n", + buf1, buf2); + fail++; + } + num++; + } +#endif printf ("%d tests failed out of %d.\n", fail, num); + return fail; } #endif /* SNPRINTF_TEST */ From 17548a3c5bc6aa6bb04c9fa71f4bc36bc5c28ebf Mon Sep 17 00:00:00 2001 From: user01010111 Date: Tue, 1 Sep 2026 10:24:40 +0000 Subject: [PATCH 2/3] common/snprintf.c: explain unsigned negation Document why fmtint negates the unsigned magnitude when formatting negative values, including LLONG_MIN. AI assistance: OpenAI Codex gpt-5.6-sol was used for repository analysis, implementation, review, drafting and validation. The human contributor reviewed the change and remains responsible for it. Signed-off-by: user01010111 --- common/snprintf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/snprintf.c b/common/snprintf.c index cf4aa2634a..74cba733e3 100644 --- a/common/snprintf.c +++ b/common/snprintf.c @@ -523,6 +523,7 @@ static void fmtint (char *buffer, size_t *currlen, size_t maxlen, { if( value < 0 ) { signvalue = '-'; + /* Negate as unsigned to handle LLONG_MIN without overflow. */ uvalue = -uvalue; } else From 9d8e1f636ec15a7cba033a5d793c8b50270f11e6 Mon Sep 17 00:00:00 2001 From: user01010111 Date: Tue, 1 Sep 2026 13:12:35 +0000 Subject: [PATCH 3/3] common/snprintf.c: keep magnitudes unsigned Carry unsigned format arguments in unsigned LLONG from dopr() through fmtint(), rather than converting them through a possibly narrower signed long. Track negativity separately for signed decimal values and keep the magnitude negation in unsigned arithmetic so LLONG_MIN remains defined. Convert pointers through size_t before widening, and extend the built-in comparisons with narrow and wide unsigned extrema. AI assistance: OpenAI Codex gpt-5.6-sol with xhigh reasoning was used for repository analysis, implementation, review, drafting and validation. Anthropic Claude Fable 5 (claude-fable-5) with xhigh reasoning performed an independent adversarial review and produced a prototype used as design evidence. The human contributor reviewed the change and remains responsible for it. Signed-off-by: user01010111 --- common/snprintf.c | 99 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 28 deletions(-) diff --git a/common/snprintf.c b/common/snprintf.c index 74cba733e3..438829ce8f 100644 --- a/common/snprintf.c +++ b/common/snprintf.c @@ -118,7 +118,8 @@ static void dopr (char *buffer, size_t maxlen, const char *format, static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, char *value, int flags, int min, int max); static void fmtint (char *buffer, size_t *currlen, size_t maxlen, - LLONG value, int base, int min, int max, int flags); + unsigned LLONG uvalue, int negative, + int base, int min, int max, int flags); static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, int min, int max, int flags); static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c ); @@ -175,6 +176,7 @@ static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) { char ch; LLONG value; + unsigned LLONG uvalue; LDOUBLE fvalue; char *strvalue; int min; @@ -313,39 +315,40 @@ static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) value = va_arg (args, LLONG); else value = va_arg (args, int); - fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + fmtint (buffer, &currlen, maxlen, (unsigned LLONG)value, + (value < 0), 10, min, max, flags); break; case 'o': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) #ifdef C89PLUS - value = (unsigned short int)va_arg (args, unsigned int); + uvalue = (unsigned short int)va_arg (args, unsigned int); #else - value = va_arg (args, unsigned short int); + uvalue = va_arg (args, unsigned short int); #endif else if (cflags == DP_C_LONG) - value = (long)va_arg (args, unsigned long int); + uvalue = va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) - value = (LLONG)va_arg (args, unsigned LLONG); + uvalue = va_arg (args, unsigned LLONG); else - value = (long)va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); + uvalue = va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, uvalue, 0, 8, min, max, flags); break; case 'u': flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) #ifdef C89PLUS - value = (unsigned short int)va_arg (args, unsigned int); + uvalue = (unsigned short int)va_arg (args, unsigned int); #else - value = va_arg (args, unsigned short int); + uvalue = va_arg (args, unsigned short int); #endif else if (cflags == DP_C_LONG) - value = (long)va_arg (args, unsigned long int); + uvalue = va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) - value = (LLONG)va_arg (args, unsigned LLONG); + uvalue = va_arg (args, unsigned LLONG); else - value = (long)va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + uvalue = va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, uvalue, 0, 10, min, max, flags); break; case 'X': flags |= DP_F_UP; @@ -355,17 +358,17 @@ static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) flags |= DP_F_UNSIGNED; if (cflags == DP_C_SHORT) #ifdef C89PLUS - value = (unsigned short int)va_arg (args, unsigned int); + uvalue = (unsigned short int)va_arg (args, unsigned int); #else - value = va_arg (args, unsigned short int); + uvalue = va_arg (args, unsigned short int); #endif else if (cflags == DP_C_LONG) - value = (long)va_arg (args, unsigned long int); + uvalue = va_arg (args, unsigned long int); else if (cflags == DP_C_LLONG) - value = (LLONG)va_arg (args, unsigned LLONG); + uvalue = va_arg (args, unsigned LLONG); else - value = (long)va_arg (args, unsigned int); - fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); + uvalue = va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, uvalue, 0, 16, min, max, flags); break; case 'f': if (cflags == DP_C_LDOUBLE) @@ -407,7 +410,8 @@ static void dopr (char *buffer, size_t maxlen, const char *format, va_list args) case 'p': strvalue = va_arg (args, void *); flags |= DP_F_UNSIGNED; - fmtint (buffer, &currlen, maxlen, (LLONG) strvalue, 16, min, max, flags); + fmtint (buffer, &currlen, maxlen, + (unsigned LLONG)(size_t) strvalue, 0, 16, min, max, flags); break; case 'n': if (cflags == DP_C_SHORT) @@ -504,11 +508,11 @@ static void fmtstr (char *buffer, size_t *currlen, size_t maxlen, /* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ static void fmtint (char *buffer, size_t *currlen, size_t maxlen, - LLONG value, int base, int min, int max, int flags) + unsigned LLONG uvalue, int negative, + int base, int min, int max, int flags) { int signvalue = 0; - unsigned LLONG uvalue; - char convert[sizeof(value) * CHAR_BIT + 1]; + char convert[sizeof(uvalue) * CHAR_BIT + 1]; int place = 0; int spadlen = 0; /* amount to space pad */ int zpadlen = 0; /* amount to zero pad */ @@ -517,13 +521,13 @@ static void fmtint (char *buffer, size_t *currlen, size_t maxlen, if (max < 0) max = 0; - uvalue = value; - if(!(flags & DP_F_UNSIGNED)) { - if( value < 0 ) { + if( negative ) { signvalue = '-'; - /* Negate as unsigned to handle LLONG_MIN without overflow. */ + /* uvalue holds the signed argument converted to unsigned + * (defined modulo arithmetic); unsigned negation yields its + * magnitude without overflow, even for LLONG_MIN. */ uvalue = -uvalue; } else @@ -946,6 +950,12 @@ static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) static int pointer_num; #ifdef HAVE_LONG_LONG_INT LLONG llong_nums[] = { ((LLONG)1 << 32) + 1, LLONG_MIN }; + char *ullong_fmt[] = { "%llu", "%llx", "%llo", NULL }; + unsigned LLONG ullong_nums[] = { + ULLONG_MAX, + (unsigned LLONG)1 << (sizeof(unsigned LLONG) * CHAR_BIT - 1), + ULLONG_MAX + }; #endif char *ptr1; char *ptr2; @@ -983,6 +993,26 @@ static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) num++; } + snprintf (buf1, sizeof (buf1), "%u", UINT_MAX); + sprintf (buf2, "%u", UINT_MAX); + if (strcmp (buf1, buf2)) + { + printf("snprintf doesn't match Format: %%u\n\tsnprintf = %s\n\tsprintf = %s\n", + buf1, buf2); + fail++; + } + num++; + + snprintf (buf1, sizeof (buf1), "%lu", ULONG_MAX); + sprintf (buf2, "%lu", ULONG_MAX); + if (strcmp (buf1, buf2)) + { + printf("snprintf doesn't match Format: %%lu\n\tsnprintf = %s\n\tsprintf = %s\n", + buf1, buf2); + fail++; + } + num++; + snprintf (buf1, sizeof (buf1), "%p", (void *)&pointer_num); sprintf (buf2, "%p", (void *)&pointer_num); ptr1 = buf1; @@ -1022,6 +1052,19 @@ static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c) } num++; } + + for (x = 0; ullong_fmt[x] != NULL; x++) + { + snprintf (buf1, sizeof (buf1), ullong_fmt[x], ullong_nums[x]); + sprintf (buf2, ullong_fmt[x], ullong_nums[x]); + if (strcmp (buf1, buf2)) + { + printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf = %s\n", + ullong_fmt[x], buf1, buf2); + fail++; + } + num++; + } #endif printf ("%d tests failed out of %d.\n", fail, num); return fail;