> "Jeff Epler" <[email protected]> writes: > > > The return type of fprintftime is `size_t`. But 4GiB+ of output can be > > generated. If GNU still supports ILP32 platforms, the return value is > > already unreliable for determining the number of bytes written. > > Good point. Yes, we try to keep things working on 32-bit platforms.
Yes, good point. Thanks for the report. > size_t is slightly better than the int used by stdio functions. Bruno > added some zprintf functions that return off64_t to avoid this > limitation [1]. I'm applying this patch, to fix the limitation also for fprintftime(). It does not need a 'z' variant, since fprintftime() is not standardized and the change won't cause compilation errors. 2025-10-30 Bruno Haible <[email protected]> fprintftime: Avoid size_t overflow for very large outputs. Reported by Jeff Epler <[email protected]> in <https://lists.gnu.org/archive/html/bug-gnulib/2025-10/msg00126.html>. * lib/fprintftime.h: Include <sys/types.h>. (fprintftime): Change return type to off64_t. * lib/strftime.c (byte_count_t): New macro. (my_strftime, __strftime_internal): Change return type to off64_t. * modules/fprintftime (Depends-on): Add sys_types-h. * NEWS: Mention the change. diff --git a/NEWS b/NEWS index de89ddb78c..55dfcd95b0 100644 --- a/NEWS +++ b/NEWS @@ -78,6 +78,8 @@ User visible incompatible changes Date Modules Changes +2025-10-30 fprintftime The return value is changed from size_t to off64_t. + 2025-08-05 git-merge-changelog This module is removed. Use the package from https://git.savannah.gnu.org/git/vc-changelog.git instead. diff --git a/lib/fprintftime.h b/lib/fprintftime.h index 9b868e8ae3..a340b86117 100644 --- a/lib/fprintftime.h +++ b/lib/fprintftime.h @@ -16,6 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include <stdio.h> +#include <sys/types.h> #include <time.h> #ifdef __cplusplus @@ -29,9 +30,11 @@ extern "C" { Output to stream FP the result of formatting (according to the nstrftime format string, FMT) the time data, *TM, and the ZONE - and NANOSECONDS values. */ -size_t fprintftime (FILE *fp, char const *fmt, struct tm const *tm, - timezone_t zone, int nanoseconds); + and NANOSECONDS values. + + Return the number of bytes written to the stream (always >= 0). */ +off64_t fprintftime (FILE *fp, char const *fmt, struct tm const *tm, + timezone_t zone, int nanoseconds); #ifdef __cplusplus diff --git a/lib/strftime.c b/lib/strftime.c index 27e2ca0976..3898a7873d 100644 --- a/lib/strftime.c +++ b/lib/strftime.c @@ -198,12 +198,17 @@ enum pad_style # define mktime(tp) __mktime64 (tp) #endif +/* For functions that fill an in-memory string, the number of bytes fits in a + size_t. For functions that write to a stream, the number of bytes fits in + an off64_t (a type that is always at least 64 bits large). */ #if FPRINTFTIME # define STREAM_OR_CHAR_T FILE # define STRFTIME_ARG(x) /* empty */ +# define byte_count_t off64_t #else # define STREAM_OR_CHAR_T CHAR_T # define STRFTIME_ARG(x) x, +# define byte_count_t size_t #endif #if FPRINTFTIME @@ -894,12 +899,13 @@ static CHAR_T const c_month_names[][sizeof "September"] = # define ns 0 #endif -static size_t __strftime_internal (STREAM_OR_CHAR_T *, STRFTIME_ARG (size_t) - const CHAR_T *, const struct tm *, - CAL_ARGS (const struct calendar *, - struct calendar_date *) - bool, enum pad_style, int, bool * - extra_args_spec LOCALE_PARAM); +static byte_count_t __strftime_internal (STREAM_OR_CHAR_T *, + STRFTIME_ARG (size_t) + const CHAR_T *, const struct tm *, + CAL_ARGS (const struct calendar *, + struct calendar_date *) + bool, enum pad_style, int, bool * + extra_args_spec LOCALE_PARAM); #if !defined _LIBC \ && (!(HAVE_ONLY_C_LOCALE || (USE_C_LOCALE && !HAVE_STRFTIME_L)) \ @@ -1107,7 +1113,7 @@ get_tm_zone (timezone_t tz, char *ubuf, int ubufsize, int modifier, characters written. If S is NULL, nothing will be written anywhere, so to determine how many characters would be written, use NULL for S and (size_t) -1 for MAXSIZE. */ -size_t +byte_count_t my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) const CHAR_T *format, const struct tm *tp extra_args_spec LOCALE_PARAM) @@ -1150,7 +1156,7 @@ libc_hidden_def (my_strftime) UPCASE indicates that the result should be converted to upper case. YR_SPEC and WIDTH specify the padding and width for the year. *TZSET_CALLED indicates whether tzset has been called here. */ -static size_t +static byte_count_t __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) const CHAR_T *format, const struct tm *tp, @@ -1221,7 +1227,7 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) # define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11)) # define ap_len 2 #endif - size_t i = 0; + byte_count_t i = 0; STREAM_OR_CHAR_T *p = s; const CHAR_T *f; #if DO_MULTIBYTE && !defined COMPILE_WIDE diff --git a/modules/fprintftime b/modules/fprintftime index 2eacd8526f..121073af1f 100644 --- a/modules/fprintftime +++ b/modules/fprintftime @@ -8,6 +8,7 @@ lib/strftime.c Depends-on: stdio-h +sys_types-h nstrftime time_rz
