[PROPOSED PATCH 1/4] Fix unlikely buffer overrun when setting date across network.
* date.c (netsettime) [TSP_SETDATE]: Don't assume gethostname returns a null-terminated string. --- date.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/date.c b/date.c index c8fab75..3b44c94 100644 --- a/date.c +++ b/date.c @@ -819,7 +819,6 @@ netsettime(struct timeval ntv) { int s, length, port, timed_ack, found, err, waittime; fd_set ready; - char hostname[MAXHOSTNAMELEN]; struct timeval tout; struct servent *sp; struct tsp msg; @@ -858,11 +857,15 @@ netsettime(struct timeval ntv) } msg.tsp_type = TSP_SETDATE; msg.tsp_vers = TSPVERSION; - if (gethostname(hostname, sizeof (hostname))) { + msg.tsp_name[sizeof msg.tsp_name - 1] = '\0'; + if (gethostname(msg.tsp_name, sizeof msg.tsp_name) != 0) { perror("gethostname"); goto bad; } - strncpy(msg.tsp_name, hostname, sizeof (hostname)); + if (msg.tsp_name[sizeof msg.tsp_name - 1]) { + fprintf(stderr, "hostname too long\n"); + goto bad; + } msg.tsp_seq = htons(0); msg.tsp_time.tv_sec = htonl(ntv.tv_sec); msg.tsp_time.tv_usec = htonl(ntv.tv_usec); -- 1.9.1
* localtime.c (tzparse) * zic.c (doabbr): Use memcpy, as only nonzero bytes are being copied. * zic.c (doabbr): Remove unnecessary test. --- localtime.c | 4 ++-- zic.c | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/localtime.c b/localtime.c index 98faacd..67b1d78 100644 --- a/localtime.c +++ b/localtime.c @@ -1148,11 +1148,11 @@ tzparse(const char *name, register struct state *const sp, if ((size_t) sp->charcnt > sizeof sp->chars) return false; cp = sp->chars; - strncpy(cp, stdname, stdlen); + memcpy(cp, stdname, stdlen); cp += stdlen; *cp++ = '\0'; if (dstlen != 0) { - strncpy(cp, dstname, dstlen); + memcpy(cp, dstname, dstlen); *(cp + dstlen) = '\0'; } return true; diff --git a/zic.c b/zic.c index 73ae17d..d622b80 100644 --- a/zic.c +++ b/zic.c @@ -1868,8 +1868,7 @@ doabbr(char *const abbr, const char *const format, const char *const letters, } else if (isdst) { strcpy(abbr, slashp + 1); } else { - if (slashp > format) - strncpy(abbr, format, slashp - format); + memcpy(abbr, format, slashp - format); abbr[slashp - format] = '\0'; } if (!doquotes) -- 1.9.1
(longest): Now int, since it can't exceed INT_MAX. (sumsize, settimezone): New functions. (main): Don't let 'longest' exceed INT_MAX. Use settimezone. (show): Remove no-longer-necessary cast that formerly had undefined behavior if 'longest' exceeded INT_MAX. --- zdump.c | 82 ++++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/zdump.c b/zdump.c index 08323c1..f5a4dff 100644 --- a/zdump.c +++ b/zdump.c @@ -207,7 +207,7 @@ static time_t const absolute_max_time = ((time_t) -1 < 0 ? - (~ 0 < 0) - ((time_t) -1 << (CHAR_BIT * sizeof (time_t) - 1)) : -1); -static size_t longest; +static int longest; static char * progname; static bool warned; static bool errout; @@ -239,6 +239,54 @@ is_alpha(char a) } } +/* Return A + B, exiting if the result would overflow. */ +static size_t +sumsize(size_t a, size_t b) +{ + size_t sum = a + b; + if (sum < a) { + fprintf(stderr, "%s: size overflow\n", progname); + exit(EXIT_FAILURE); + } + return sum; +} + +/* Set the global time zone to VAL, exiting on memory allocation failure. */ +static void +settimezone(char const *val) +{ + static char **fakeenv; + char **env = fakeenv; + char *oldstorage = env ? env[0] : 0; + char *env0; + if (! env) { + char **e = environ; + int to; + + while (*e++) + continue; + env = malloc(sumsize(sizeof *environ, + (e - environ) * sizeof *environ)); + if (! env) { + perror(progname); + exit(EXIT_FAILURE); + } + to = 1; + for (e = environ; (env[to] = *e); e++) + to += strncmp(*e, "TZ=", 3) != 0; + } + env0 = malloc(sumsize(sizeof "TZ=", strlen(val))); + if (! env0) { + perror(progname); + exit(EXIT_FAILURE); + } + strcpy(env0, "TZ="); + strcat(env0, val); + env[0] = env0; + environ = fakeenv = env; + free(oldstorage); +} + #ifndef TYPECHECK #define my_localtime localtime #else /* !defined TYPECHECK */ @@ -345,7 +393,6 @@ main(int argc, char *argv[]) register char * cuttimes; register time_t cutlotime; register time_t cuthitime; - register char ** fakeenv; time_t now; time_t t; time_t newt; @@ -446,33 +493,16 @@ main(int argc, char *argv[]) } now = time(NULL); longest = 0; - for (i = optind; i < argc; ++i) - if (strlen(argv[i]) > longest) - longest = strlen(argv[i]); - { - register int from; - register int to; - - for (i = 0; environ[i] != NULL; ++i) - continue; - fakeenv = malloc((i + 2) * sizeof *fakeenv); - if (fakeenv == NULL - || (fakeenv[0] = malloc(longest + 4)) == NULL) { - perror(progname); - return EXIT_FAILURE; - } - to = 0; - strcpy(fakeenv[to++], "TZ="); - for (from = 0; environ[from] != NULL; ++from) - if (strncmp(environ[from], "TZ=", 3) != 0) - fakeenv[to++] = environ[from]; - fakeenv[to] = NULL; - environ = fakeenv; + for (i = optind; i < argc; i++) { + size_t arglen = strlen(argv[i]); + if (longest < arglen) + longest = arglen < INT_MAX ? arglen : INT_MAX; } + for (i = optind; i < argc; ++i) { static char buf[MAX_STRING_LENGTH]; - strcpy(&fakeenv[0][3], argv[i]); + settimezone(argv[i]); if (! (vflag | Vflag)) { show(argv[i], now, false); continue; @@ -646,7 +676,7 @@ show(char *zone, time_t t, bool v) { register struct tm * tmp; - printf("%-*s ", (int) longest, zone); + printf("%-*s ", longest, zone); if (v) { tmp = gmtime(&t); if (tmp == NULL) { -- 1.9.1
Instead, allocate a longer buffer, exiting if memory is exhausted. * zdump.c (abbrev, abbrevsize, loab, loabsize): New static vars. (saveabbr): New function. (main, loab): Use the new function and variables. (abbr): Arg is now a pointer-to-const, for saveabbr. --- zdump.c | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/zdump.c b/zdump.c index f5a4dff..76b3453 100644 --- a/zdump.c +++ b/zdump.c @@ -212,7 +212,7 @@ static char * progname; static bool warned; static bool errout; -static char *abbr(struct tm *); +static char *abbr(struct tm const *); static intmax_t delta(struct tm *, struct tm *) ATTRIBUTE_PURE; static void dumptime(struct tm const *); static time_t hunt(char *, time_t, time_t); @@ -359,6 +359,36 @@ abbrok(const char *const abbrp, const char *const zone) warned = errout = true; } +/* Extensible buffers for time zone abbreviations of the current and + low-boundary probes. */ +static char *abbrev; +static size_t abbrevsize; +static char *loab; +static size_t loabsize; + +/* Save into *BUF (of size *BUFALLOC) the time zone abbreviation of TMP. + Exit on memory allocation failure. */ +static void +saveabbr(char **buf, size_t *bufalloc, struct tm const *tmp) +{ + char const *ab = abbr(tmp); + size_t ablen = strlen(ab); + if (*bufalloc <= ablen) { + free(*buf); + + /* Make the new buffer at least twice as long as the old, + to avoid O(N**2) behavior on repeated calls. */ + *bufalloc = sumsize(*bufalloc, ablen + 1); + + *buf = malloc(*bufalloc); + if (! *buf) { + perror(progname); + exit(EXIT_FAILURE); + } + } + strcpy(*buf, ab); +} + static void close_file(FILE *stream) { @@ -500,8 +530,6 @@ main(int argc, char *argv[]) } for (i = optind; i < argc; ++i) { - static char buf[MAX_STRING_LENGTH]; - settimezone(argv[i]); if (! (vflag | Vflag)) { show(argv[i], now, false); @@ -519,7 +547,7 @@ main(int argc, char *argv[]) tmp = my_localtime(&t); if (tmp != NULL) { tm = *tmp; - strncpy(buf, abbr(&tm), (sizeof buf) - 1); + saveabbr(&abbrev, &abbrevsize, &tm); } for ( ; ; ) { newt = (t < absolute_max_time - SECSPERDAY / 2 @@ -533,14 +561,13 @@ main(int argc, char *argv[]) if ((tmp == NULL || newtmp == NULL) ? (tmp != newtmp) : (delta(&newtm, &tm) != (newt - t) || newtm.tm_isdst != tm.tm_isdst || - strcmp(abbr(&newtm), buf) != 0)) { + strcmp(abbr(&newtm), abbrev) != 0)) { newt = hunt(argv[i], t, newt); newtmp = localtime(&newt); if (newtmp != NULL) { newtm = *newtmp; - strncpy(buf, - abbr(&newtm), - (sizeof buf) - 1); + saveabbr(&abbrev, &abbrevsize, + &newtm); } } t = newt; @@ -612,12 +639,11 @@ hunt(char *name, time_t lot, time_t hit) register struct tm * lotmp; struct tm tm; register struct tm * tmp; - char loab[MAX_STRING_LENGTH]; lotmp = my_localtime(&lot); if (lotmp != NULL) { lotm = *lotmp; - strncpy(loab, abbr(&lotm), (sizeof loab) - 1); + saveabbr(&loab, &loabsize, &lotm); } for ( ; ; ) { time_t diff = hit - lot; @@ -705,7 +731,7 @@ show(char *zone, time_t t, bool v) } static char * -abbr(struct tm *tmp) +abbr(struct tm const *tmp) { register char * result; static char nada; -- 1.9.1
Paul Eggert wrote:
+static char *abbrev; +static size_t abbrevsize; +static char *loab; +static size_t loabsize;
As each of these variables is used in just one function, it's slightly cleaner to move the variable from the top level to its function's body.
participants (1)
-
Paul Eggert