"Olson, Arthur David (NIH/NCI)" <olsona@dc37a.nci.nih.gov> writes:
if (y >= -999 && y <= 9999) (void) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %ld\n", wn, mn, timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, y); else (void) sprintf(buf, "%.3s %.3s%3d %ld\n", wn, mn, timeptr->tm_mday, y);
...never returns NULL and never (when applied to a "struct tm" derived from a 64-bit time_t) overflows a 26-character buffer passed to asctime.
True, but it would still overflow the 26-byte buffer in other cases, e.g., if tm_mday is out of range. And even if you had a valid struct tm, it would still overflow in some currently-theoretical environments, e.g., 64-bit int and 128-bit long and time_t. (While we're fixing this, let's fix it permanently. :-) Also, I'd rather have asctime_r return NULL when the result doesn't fit. That's what POSIX says to do when asctime_r fails, and it's what HP-UX asctime_r does. If you want asctime_r to avoid overflowing a 26-byte buffer, the simplest way is to use snprintf instead of sprintf. Something like the following should do the trick. =================================================================== RCS file: RCS/Makefile,v retrieving revision 2004.1 retrieving revision 2004.1.0.1 diff -pu -r2004.1 -r2004.1.0.1 --- Makefile 2004/03/19 19:48:35 2004.1 +++ Makefile 2004/07/22 20:05:59 2004.1.0.1 @@ -96,6 +96,7 @@ LDLIBS= # -DHAVE_SETTIMEOFDAY=1 if settimeofday has just 1 arg (SVR4) # -DHAVE_SETTIMEOFDAY=2 if settimeofday uses 2nd arg (4.3BSD) # -DHAVE_SETTIMEOFDAY=3 if settimeofday ignores 2nd arg (4.4BSD) +# -DHAVE_SNPRINTF=0 if your system lacks the snprintf function # -DHAVE_STRERROR=0 if your system lacks the strerror function # -DHAVE_SYMLINK=0 if your system lacks the symlink function # -DHAVE_SYS_STAT_H=0 if your compiler lacks a "sys/stat.h" =================================================================== RCS file: RCS/private.h,v retrieving revision 2003.5 retrieving revision 2003.5.0.1 diff -pu -r2003.5 -r2003.5.0.1 --- private.h 2003/12/15 14:36:35 2003.5 +++ private.h 2004/07/22 20:06:11 2003.5.0.1 @@ -46,6 +46,10 @@ static char privatehid[] = "@(#)private. #define HAVE_SETTIMEOFDAY 3 #endif /* !defined HAVE_SETTIMEOFDAY */ +#ifndef HAVE_SNPRINTF +#define HAVE_SNPRINTF 1 +#endif /* !defined HAVE_STRERROR */ + #ifndef HAVE_STRERROR #define HAVE_STRERROR 1 #endif /* !defined HAVE_STRERROR */ =================================================================== RCS file: RCS/asctime.c,v retrieving revision 2004.1 retrieving revision 2004.1.0.2 diff -pu -r2004.1 -r2004.1.0.2 --- asctime.c 1998/05/28 13:56:06 2004.1 +++ asctime.c 2004/07/22 20:03:28 2004.1.0.2 @@ -14,14 +14,52 @@ static char elsieid[] = "@(#)asctime.c 7 #include "private.h" #include "tzfile.h" +#ifndef EOVERFLOW +# define EOVERFLOW EINVAL +#endif + /* ** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, Second Edition, 1996-07-12. */ -char * -asctime_r(timeptr, buf) +/* +** Big enough for something such as +** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n +** (two three-character abbreviations, five strings denoting integers, +** three explicit spaces, two explicit colons, a newline, +** and a trailing ASCII nul). +*/ +#define MAX_ASCTIME_SIZE (3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) + 3 + 2 + 1 + 1) + +#if !HAVE_SNPRINTF +/* +** A substitute for snprintf that is good enough for asctime. +*/ +static int +snprintf(buf, size, format, mday, hour, min, sec, year) +char * buf; +size_t size; +const char * format; +int mday, hour, min, sec; +long year; +{ + char tbuf[MAX_ASCTIME_SIZE]; + size_t len; + (void) sprintf(tbuf, buf, size, format, mday, hour, min, sec, year); + len = strlen(tbuf); + if (len < size) { + (void) strcpy(buf, tbuf); + return len; + } else + return -1; +} +#endif + +static char * +asctime_rn(timeptr, buf, size) register const struct tm * timeptr; char * buf; +size_t size; { static const char wday_name[][3] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" @@ -41,17 +79,29 @@ char * buf; else mn = mon_name[timeptr->tm_mon]; /* ** The X3J11-suggested format is - ** "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n" - ** Since the .2 in 02.2d is ignored, we drop it. + ** "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n" + ** Use "%02d", as it is a bit more portable than "%.2d". */ - (void) sprintf(buf, "%.3s %.3s%3d %02d:%02d:%02d %d\n", + if (snprintf(buf, size, "%.3s %.3s%3d %02d:%02d:%02d %ld\n", wn, mn, timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, - TM_YEAR_BASE + timeptr->tm_year); + timeptr->tm_year + (long) TM_YEAR_BASE) + < 0) { + errno = EOVERFLOW; + return NULL; + } return buf; } +char * +asctime_r(timeptr, buf) +register const struct tm * timeptr; +char * buf; +{ + return asctime_rn(timeptr, buf, 26); +} + /* ** A la X3J11, with core dump avoidance. */ @@ -60,15 +110,7 @@ char * asctime(timeptr) register const struct tm * timeptr; { - /* - ** Big enough for something such as - ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n - ** (two three-character abbreviations, five strings denoting integers, - ** three explicit spaces, two explicit colons, a newline, - ** and a trailing ASCII nul). - */ - static char result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) + - 3 + 2 + 1 + 1]; + static char result[MAX_ASCTIME_SIZE]; - return asctime_r(timeptr, result); + return asctime_rn(timeptr, result, sizeof result); }