* lib/strftime.c: Include <stdint.h>, for PTRDIFF_MAX. (byte_count_t): Now a typedef instead of a macro. (sbyte_count_t): New typedef. (SBYTE_COUNT_MAX): New macro. (memset_byte, width_add, __strftime_internal): (fwrite_lowcase, fwrite_uppcase) [FPRINTFTIME]: Avoid overflow issues with fprintftime by using byte_count_t or sbyte_count_t instead of size_t or int. * modules/nstrftime: Depend on stdint-h. --- ChangeLog | 12 +++++++++ lib/strftime.c | 68 ++++++++++++++++++++++++++++------------------- modules/nstrftime | 1 + 3 files changed, 54 insertions(+), 27 deletions(-)
diff --git a/ChangeLog b/ChangeLog index a1881fa7ab..524957585f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2025-10-30 Paul Eggert <[email protected]> + + fprintftime: sbyte_count_t to fix some overflows + * lib/strftime.c: Include <stdint.h>, for PTRDIFF_MAX. + (byte_count_t): Now a typedef instead of a macro. + (sbyte_count_t): New typedef. + (SBYTE_COUNT_MAX): New macro. + (memset_byte, width_add, b__strftime_internal): + Avoid overflow issues with fprintftime by using byte_count_t + or sbyte_count_t instead of size_t or int. + * modules/nstrftime: Depend on stdint-h. + 2025-10-30 Eric Wong <[email protected]> (tiny change) libc-config: Don't disable __attribute__ when compiling with Tiny C. diff --git a/lib/strftime.c b/lib/strftime.c index 3898a7873d..4c3ae7b984 100644 --- a/lib/strftime.c +++ b/lib/strftime.c @@ -110,6 +110,7 @@ #include <locale.h> #include <stdckdint.h> #include <stddef.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> @@ -204,16 +205,24 @@ enum pad_style #if FPRINTFTIME # define STREAM_OR_CHAR_T FILE # define STRFTIME_ARG(x) /* empty */ -# define byte_count_t off64_t +typedef off64_t byte_count_t, sbyte_count_t; +# define SBYTE_COUNT_MAX 0x7fffffffffffffff #else # define STREAM_OR_CHAR_T CHAR_T # define STRFTIME_ARG(x) x, -# define byte_count_t size_t +typedef size_t byte_count_t; +typedef ptrdiff_t sbyte_count_t; +# define SBYTE_COUNT_MAX PTRDIFF_MAX #endif #if FPRINTFTIME # define memset_byte(P, Len, Byte) \ - do { size_t _i; for (_i = 0; _i < Len; _i++) fputc (Byte, P); } while (0) + do \ + { \ + for (byte_count_t _i = Len; 0 < _i; _i--) \ + fputc (Byte, P); \ + } \ + while (false) # define memset_space(P, Len) memset_byte (P, Len, ' ') # define memset_zero(P, Len) memset_byte (P, Len, '0') #elif defined COMPILE_WIDE @@ -234,9 +243,9 @@ enum pad_style #define width_add(width, n, f) \ do \ { \ - size_t _n = (n); \ - size_t _w = pad == NO_PAD || width < 0 ? 0 : width; \ - size_t _incr = _n < _w ? _w : _n; \ + byte_count_t _n = n; \ + byte_count_t _w = pad == NO_PAD || width < 0 ? 0 : width; \ + byte_count_t _incr = _n < _w ? _w : _n; \ if (_incr >= maxsize - i) \ { \ errno = ERANGE; \ @@ -246,7 +255,7 @@ enum pad_style { \ if (_n < _w) \ { \ - size_t _delta = _w - _n; \ + byte_count_t _delta = _w - _n; \ if (pad == ALWAYS_ZERO_PAD || pad == SIGN_PAD) \ memset_zero (p, _delta); \ else \ @@ -367,7 +376,7 @@ enum pad_style #if FPRINTFTIME static void -fwrite_lowcase (FILE *fp, const CHAR_T *src, size_t len) +fwrite_lowcase (FILE *fp, const CHAR_T *src, off64_t len) { while (len-- > 0) { @@ -377,7 +386,7 @@ fwrite_lowcase (FILE *fp, const CHAR_T *src, size_t len) } static void -fwrite_uppcase (FILE *fp, const CHAR_T *src, size_t len) +fwrite_uppcase (FILE *fp, const CHAR_T *src, off64_t len) { while (len-- > 0) { @@ -904,7 +913,8 @@ static byte_count_t __strftime_internal (STREAM_OR_CHAR_T *, const CHAR_T *, const struct tm *, CAL_ARGS (const struct calendar *, struct calendar_date *) - bool, enum pad_style, int, bool * + bool, enum pad_style, + sbyte_count_t, bool * extra_args_spec LOCALE_PARAM); #if !defined _LIBC \ @@ -1108,11 +1118,12 @@ get_tm_zone (timezone_t tz, char *ubuf, int ubufsize, int modifier, } /* Write information from TP into S according to the format - string FORMAT, writing no more that MAXSIZE characters - (including the terminating '\0') and returning number of - 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. */ + string FORMAT. Return the humber of bytes written. + + If !FPRINTFTIME, write no more than MAXSIZE bytes (including the + terminating '\0'), and if S is NULL do not write into S. + To determine how many characters would be written, use NULL for S + and (size_t) -1 for MAXSIZE. */ byte_count_t my_strftime (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) const CHAR_T *format, @@ -1163,14 +1174,15 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) CAL_ARGS (const struct calendar *cal, struct calendar_date *caldate) bool upcase, - enum pad_style yr_spec, int width, bool *tzset_called + enum pad_style yr_spec, sbyte_count_t width, + bool *tzset_called extra_args_spec LOCALE_PARAM) { #if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL struct __locale_data *const current = loc->__locales[LC_TIME]; #endif #if FPRINTFTIME - size_t maxsize = (size_t) -1; + byte_count_t maxsize = SBYTE_COUNT_MAX; #endif int saved_errno = errno; @@ -1266,7 +1278,7 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) size_t colons; bool change_case = false; int format_char; - int subwidth; + sbyte_count_t subwidth; #if DO_MULTIBYTE && !defined COMPILE_WIDE switch (*f) @@ -1390,7 +1402,7 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) { if (ckd_mul (&width, width, 10) || ckd_add (&width, width, *f - L_('0'))) - width = INT_MAX; + width = SBYTE_COUNT_MAX; ++f; } while (ISDIGIT (*f)); @@ -1591,12 +1603,13 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) subwidth = -1; subformat_width: { - size_t len = __strftime_internal (NULL, STRFTIME_ARG ((size_t) -1) - subfmt, tp, - CAL_ARGS (cal, caldate) - to_uppcase, pad, subwidth, - tzset_called - extra_args LOCALE_ARG); + byte_count_t len = + __strftime_internal (NULL, STRFTIME_ARG ((size_t) -1) + subfmt, tp, + CAL_ARGS (cal, caldate) + to_uppcase, pad, subwidth, + tzset_called + extra_args LOCALE_ARG); add (len, __strftime_internal (p, STRFTIME_ARG (maxsize - i) subfmt, tp, @@ -1868,8 +1881,9 @@ __strftime_internal (STREAM_OR_CHAR_T *s, STRFTIME_ARG (size_t maxsize) if (digits_base >= 0x100) number_digits = number_bytes / 2; #endif - int shortage = width - !!sign_char - number_digits; - int padding = pad == NO_PAD || shortage <= 0 ? 0 : shortage; + byte_count_t shortage = width - !!sign_char - number_digits; + byte_count_t padding = (pad == NO_PAD || shortage <= 0 + ? 0 : shortage); if (sign_char) { diff --git a/modules/nstrftime b/modules/nstrftime index aa1740ebc4..ee52e89825 100644 --- a/modules/nstrftime +++ b/modules/nstrftime @@ -25,6 +25,7 @@ localcharset localename-unsafe bool stdckdint-h +stdint-h time_rz configure.ac: -- 2.51.0
