> "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
 




Reply via email to