From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Paul Eggert Newsgroups: gmane.emacs.bugs Subject: Emacs current-time-string core dump on 64-bit hosts Date: Thu, 16 Mar 2006 21:58:09 -0800 Message-ID: NNTP-Posting-Host: main.gmane.org X-Trace: sea.gmane.org 1142578602 16168 80.91.229.2 (17 Mar 2006 06:56:42 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Fri, 17 Mar 2006 06:56:42 +0000 (UTC) Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Fri Mar 17 07:56:39 2006 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by ciao.gmane.org with esmtp (Exim 4.43) id 1FK8t1-00049q-Ga for geb-bug-gnu-emacs@m.gmane.org; Fri, 17 Mar 2006 07:56:36 +0100 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1FK8t1-0001i0-3s for geb-bug-gnu-emacs@m.gmane.org; Fri, 17 Mar 2006 01:56:35 -0500 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1FK7ya-0003h7-Hp for bug-gnu-emacs@gnu.org; Fri, 17 Mar 2006 00:58:16 -0500 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1FK7yW-0003gF-Au for bug-gnu-emacs@gnu.org; Fri, 17 Mar 2006 00:58:15 -0500 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1FK7yW-0003gC-3x for bug-gnu-emacs@gnu.org; Fri, 17 Mar 2006 00:58:12 -0500 Original-Received: from [131.179.128.19] (helo=kiwi.cs.ucla.edu) by monty-python.gnu.org with esmtp (Exim 4.52) id 1FK83J-00018U-W3 for bug-gnu-emacs@gnu.org; Fri, 17 Mar 2006 01:03:10 -0500 Original-Received: from penguin.cs.ucla.edu (Penguin.CS.UCLA.EDU [131.179.64.200]) by kiwi.cs.ucla.edu (8.11.7p1+Sun/8.11.7/UCLACS-5.2) with ESMTP id k2H5w9810947 for ; Thu, 16 Mar 2006 21:58:09 -0800 (PST) Original-Received: from eggert by penguin.cs.ucla.edu with local (Exim 4.50) id 1FK7yT-0005uJ-He for bug-gnu-emacs@gnu.org; Thu, 16 Mar 2006 21:58:09 -0800 Original-To: bug-gnu-emacs@gnu.org X-Draft-From: ("nnml:mail.misc" "") Gcc: nnfolder+archive:outgo X-Mailman-Approved-At: Fri, 17 Mar 2006 01:56:33 -0500 X-BeenThere: bug-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.bugs:14956 Archived-At: GNU Emacs 21.4.1 (sparc-sun-solaris2.8, X toolkit), built with "CC='gcc -m64' ./configure" to get 64-bit mode, reliably dumps core when I execute (current-time-string '(2814749767106 0)), due to a buffer overrun inside the C library. This is because Emacs is relying on undefined behavior within the ctime() call. I discovered this problem after reporting a bug in the POSIX specification for ctime; see . In practice, ctime (as well as ctime_r, asctime, and asctime_r) cannot be trusted with arbitrary time stamps, any more than gets can be trusted with arbitrary inputs: if the time stamps generate a string that is too long, the program can do anything. Here is a proposed patch, relative to CVS head. 2006-03-16 Paul Eggert Do not use ctime, since it has undefined behavior with out-of-range time stamps. This fixes a bug where (current-time-string '(2814749767106 0)) would make Emacs dump core on 64-bit Solaris 8; the fix is to remove all uses of ctime from the Emacs source code. Please see for more details about this portability problem. * lib-src/b2m.c: Include , for CHAR_BIT. (TM_YEAR_BASE): New macro. (string_from_time): New function. (main): Use it instead of ctime, to avoid buffer overflows with outlandish time stamps. * lib-src/fakemail.c: Likewise. * src/editfns.c: Include , for CHAR_BIT. (TM_YEAR_BASE): Move up, so new code can use it. (Fdecode_time, Fencode_time): Use TM_YEAR_BASE instead of hardwiring 1900. (Fdecode_time): Cast tm_year to EMACS_INT, to avoid overflow when int is narrow than EMACS_INT. (Fcurrent_time_string): Do not use ctime, since it might cause a buffer overflow with outlandish time stamps. Instead, implement its algorithm ourselves. As with Fformat_time_string, report an invalid time specification if the argument is invalid, and report an out-of-range time stamp if that problem occurs. This is better than overrunning the buffer, and it preserves the historic behavior of always returning a fixed-size string. * lib-src/ntlib.c (sys_ctime): Remove, since Emacs never calls ctime any more. * lib-src/ntlib.h (ctime): Likewise. * src/w32.c (sys_ctime): Likewise. * src/s/ms-w32.h (ctime): Likewise. Index: lib-src/b2m.c =================================================================== RCS file: /cvsroot/emacs/emacs/lib-src/b2m.c,v retrieving revision 1.30 diff -p -u -r1.30 b2m.c --- lib-src/b2m.c 7 May 2004 15:26:21 -0000 1.30 +++ lib-src/b2m.c 17 Mar 2006 05:22:00 -0000 @@ -26,6 +26,7 @@ #undef static #endif +#include #include #include #include @@ -68,6 +69,35 @@ void fatal (); #define xnew(n, Type) ((Type *) xmalloc ((n) * sizeof (Type))) +#define TM_YEAR_BASE 1900 + +static char * +string_from_time (time_t t) +{ + static char buf[sizeof "Sun Sep 16 01:03:52 " + + sizeof (long int) * CHAR_BIT / 3]; + struct tm const *tm = localtime (&t); + if (! tm) + sprintf (buf, "@%ld", (long int) t); + else + { + static char const wday[][4] = + { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static char const mon[][4] = + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + sprintf (buf, "%s %s %2d %.2d:%.2d:%.2d %ld", + wday[tm->tm_wday], mon[tm->tm_mon], tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + TM_YEAR_BASE + (long int) tm->tm_year); + } + return buf; +} + char *progname; @@ -131,7 +161,7 @@ main (argc, argv) labels_saved = printing = header = FALSE; ltoday = time (0); - today = ctime (<oday); + today = string_from_time (ltoday); data.size = 200; data.buffer = xnew (200, char); @@ -144,7 +174,7 @@ main (argc, argv) if (streq (data.buffer, "*** EOOH ***") && !printing) { printing = header = TRUE; - printf ("From \"Babyl to mail by %s\" %s", progname, today); + printf ("From \"Babyl to mail by %s\" %s\n", progname, today); continue; } Index: lib-src/fakemail.c =================================================================== RCS file: /cvsroot/emacs/emacs/lib-src/fakemail.c,v retrieving revision 1.35 diff -p -u -r1.35 fakemail.c --- lib-src/fakemail.c 6 Feb 2006 11:28:28 -0000 1.35 +++ lib-src/fakemail.c 17 Mar 2006 05:22:01 -0000 @@ -53,6 +53,7 @@ main () #include "ntlib.h" #endif +#include #include #include #include @@ -349,22 +350,47 @@ add_field (the_list, field, where) return where; } +#define TM_YEAR_BASE 1900 + +static char * +string_from_time (time_t t) +{ + static char buf[sizeof "Sun Sep 16 01:03:52 " + + sizeof (long int) * CHAR_BIT / 3]; + struct tm const *tm = localtime (&t); + if (! tm) + sprintf (buf, "@%ld", (long int) t); + else + { + static char const wday[][4] = + { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static char const mon[][4] = + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + sprintf (buf, "%s %s %2d %.2d:%.2d:%.2d %ld", + wday[tm->tm_wday], mon[tm->tm_mon], tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + TM_YEAR_BASE + (long int) tm->tm_year); + } + return buf; +} + line_list make_file_preface () { char *the_string, *temp; - long idiotic_interface; long prefix_length; long user_length; long date_length; line_list result; prefix_length = strlen (FROM_PREFIX); - time (&idiotic_interface); - the_date = ctime (&idiotic_interface); - /* the_date has an unwanted newline at the end */ - date_length = strlen (the_date) - 1; - the_date[date_length] = '\0'; + the_date = string_from_time (time (NULL)); + date_length = strlen (the_date); temp = cuserid ((char *) NULL); user_length = strlen (temp); the_user = alloc_string (user_length + 1); Index: src/editfns.c =================================================================== RCS file: /cvsroot/emacs/emacs/src/editfns.c,v retrieving revision 1.409 diff -p -u -r1.409 editfns.c --- src/editfns.c 7 Feb 2006 09:08:53 -0000 1.409 +++ src/editfns.c 17 Mar 2006 05:22:01 -0000 @@ -49,6 +49,7 @@ Boston, MA 02110-1301, USA. */ #endif #include +#include #include "intervals.h" #include "buffer.h" @@ -72,6 +73,8 @@ Boston, MA 02110-1301, USA. */ extern char **environ; #endif +#define TM_YEAR_BASE 1900 + extern size_t emacs_strftimeu P_ ((char *, size_t, const char *, const struct tm *, int)); static int tm_diff P_ ((struct tm *, struct tm *)); @@ -1722,7 +1725,7 @@ DOW and ZONE.) */) XSETFASTINT (list_args[2], decoded_time->tm_hour); XSETFASTINT (list_args[3], decoded_time->tm_mday); XSETFASTINT (list_args[4], decoded_time->tm_mon + 1); - XSETINT (list_args[5], decoded_time->tm_year + 1900); + XSETINT (list_args[5], TM_YEAR_BASE + (EMACS_INT) decoded_time->tm_year); XSETFASTINT (list_args[6], decoded_time->tm_wday); list_args[7] = (decoded_time->tm_isdst)? Qt : Qnil; @@ -1778,7 +1781,7 @@ usage: (encode-time SECOND MINUTE HOUR D tm.tm_hour = XINT (args[2]); tm.tm_mday = XINT (args[3]); tm.tm_mon = XINT (args[4]) - 1; - tm.tm_year = XINT (args[5]) - 1900; + tm.tm_year = XINT (args[5]) - TM_YEAR_BASE; tm.tm_isdst = -1; if (CONSP (zone)) @@ -1843,21 +1846,34 @@ but this is considered obsolete. */) Lisp_Object specified_time; { time_t value; - char buf[30]; - register char *tem; + char buf[sizeof "Sun Sep 16 01:03:52 1973"]; + struct tm const *tm; + static char const wday[][4] = + { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }; + static char const mon[][4] = + { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; if (! lisp_time_argument (specified_time, &value, NULL)) - value = -1; - tem = (char *) ctime (&value); + error ("Invalid time specification"); - strncpy (buf, tem, 24); - buf[24] = 0; + tm = localtime (&value); + if (! (tm + && -999 - TM_YEAR_BASE <= tm->tm_year + && tm->tm_year <= 9999 - TM_YEAR_BASE)) + error ("Specified time is not representable"); + sprintf (buf, "%s %s %2d %02d:%02d:%02d %04d", + wday[tm->tm_wday], mon[tm->tm_mon], tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + TM_YEAR_BASE + tm->tm_year); return build_string (buf); } -#define TM_YEAR_BASE 1900 - /* Yield A - B, measured in seconds. This function is copied from the GNU C Library. */ static int Index: lib-src/ntlib.c =================================================================== RCS file: /cvsroot/emacs/emacs/lib-src/ntlib.c,v retrieving revision 1.13 diff -p -u -r1.13 ntlib.c --- lib-src/ntlib.c 6 Feb 2006 11:28:28 -0000 1.13 +++ lib-src/ntlib.c 17 Mar 2006 05:22:01 -0000 @@ -188,16 +188,6 @@ fchown (int fd, int uid, int gid) return 0; } -/* Place a wrapper around the MSVC version of ctime. It returns NULL - on network directories, so we handle that case here. - (Ulrich Leodolter, 1/11/95). */ -char * -sys_ctime (const time_t *t) -{ - char *str = (char *) ctime (t); - return (str ? str : "Sun Jan 01 00:00:00 1970"); -} - FILE * sys_fopen(const char * path, const char * mode) { Index: lib-src/ntlib.h =================================================================== RCS file: /cvsroot/emacs/emacs/lib-src/ntlib.h,v retrieving revision 1.11 diff -p -u -r1.11 ntlib.h --- lib-src/ntlib.h 6 Feb 2006 11:28:28 -0000 1.11 +++ lib-src/ntlib.h 17 Mar 2006 05:22:01 -0000 @@ -61,7 +61,6 @@ int fchown (int fd, int uid, int gid); #define close _close #undef creat #define creat _creat -#undef ctime #undef dup #define dup _dup #undef dup2 Index: src/w32.c =================================================================== RCS file: /cvsroot/emacs/emacs/src/w32.c,v retrieving revision 1.100 diff -p -u -r1.100 w32.c --- src/w32.c 27 Feb 2006 02:07:37 -0000 1.100 +++ src/w32.c 17 Mar 2006 05:22:01 -0000 @@ -1325,16 +1325,6 @@ gettimeofday (struct timeval *tv, struct /* IO support and wrapper functions for W32 API. */ /* ------------------------------------------------------------------------- */ -/* Place a wrapper around the MSVC version of ctime. It returns NULL - on network directories, so we handle that case here. - (Ulrich Leodolter, 1/11/95). */ -char * -sys_ctime (const time_t *t) -{ - char *str = (char *) ctime (t); - return (str ? str : "Sun Jan 01 00:00:00 1970"); -} - /* Emulate sleep...we could have done this with a define, but that would necessitate including windows.h in the files that used it. This is much easier. */ Index: src/s/ms-w32.h =================================================================== RCS file: /cvsroot/emacs/emacs/src/s/ms-w32.h,v retrieving revision 1.37 diff -p -u -r1.37 ms-w32.h --- src/s/ms-w32.h 6 Feb 2006 15:23:23 -0000 1.37 +++ src/s/ms-w32.h 17 Mar 2006 05:22:01 -0000 @@ -317,7 +317,6 @@ Boston, MA 02110-1301, USA. */ #define close sys_close #undef creat #define creat sys_creat -#define ctime sys_ctime #undef dup #define dup sys_dup #undef dup2