bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Emacs current-time-string core dump on 64-bit hosts


From: Paul Eggert
Subject: Emacs current-time-string core dump on 64-bit hosts
Date: Fri, 17 Mar 2006 00:02:13 -0800
User-agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux)

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
<http://www.opengroup.org/austin/mailarchives/ag/msg09294.html>.
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  <address@hidden>

        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
        <http://www.opengroup.org/austin/mailarchives/ag/msg09294.html>
        for more details about this portability problem.

        * lib-src/b2m.c: Include <limits.h>, 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 <limits.h>, 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 <limits.h>
 #include <stdio.h>
 #include <time.h>
 #include <sys/types.h>
@@ -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 (&ltoday);
+  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 <limits.h>
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
@@ -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 <ctype.h>
+#include <limits.h>
 
 #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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]