emacs-diffs
[Top][All Lists]
Advanced

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

[Emacs-diffs] /srv/bzr/emacs/trunk r103643: Improve quality of tests for


From: Paul Eggert
Subject: [Emacs-diffs] /srv/bzr/emacs/trunk r103643: Improve quality of tests for time stamp overflow.
Date: Sat, 12 Mar 2011 22:43:00 -0800
User-agent: Bazaar (2.0.3)

------------------------------------------------------------
revno: 103643 [merge]
committer: Paul Eggert <address@hidden>
branch nick: trunk
timestamp: Sat 2011-03-12 22:43:00 -0800
message:
  Improve quality of tests for time stamp overflow.
modified:
  src/ChangeLog
  src/deps.mk
  src/dired.c
  src/editfns.c
  src/systime.h
=== modified file 'src/ChangeLog'
--- a/src/ChangeLog     2011-03-12 19:19:47 +0000
+++ b/src/ChangeLog     2011-03-13 06:43:00 +0000
@@ -1,3 +1,35 @@
+2011-03-13  Paul Eggert  <address@hidden>
+
+       Improve quality of tests for time stamp overflow.
+       For example, without this patch (encode-time 0 0 0 1 1
+       1152921504606846976) returns the obviously-bogus value (-948597
+       62170) on my RHEL 5.5 x86-64 host.  With the patch, it correctly
+       reports time overflow.  See
+       <http://lists.gnu.org/archive/html/emacs-devel/2011-03/msg00470.html>.
+       * deps.mk (editfns.o): Depend on ../lib/intprops.h.
+       * editfns.c: Include limits.h and intprops.h.
+       (TIME_T_MIN, TIME_T_MAX): New macros.
+       (time_overflow): Move earlier, to before first use.
+       (hi_time, lo_time): New functions, for an accurate test for
+       out-of-range times.
+       (Fcurrent_time, Fget_internal_run_time, make_time): Use them.
+       (Fget_internal_run_time): Don't assume time_t fits in int.
+       (make_time): Use list2 instead of Fcons twice.
+       (Fdecode_time): More accurate test for out-of-range times.
+       (check_tm_member): New function.
+       (Fencode_time): Use it, to test for out-of-range times.
+       (lisp_time_argument): Don't rely on undefined left-shift and
+       right-shift behavior when checking for time stamp overflow.
+
+       * editfns.c (time_overflow): New function, refactoring common code.
+       (Fformat_time_string, Fdecode_time, Fencode_time):
+       (Fcurrent_time_string): Use it.
+
+       Move 'make_time' to be next to its inverse 'lisp_time_argument'.
+       * dired.c (make_time): Move to ...
+       * editfns.c (make_time): ... here.
+       * systime.h: Note the move.
+
 2011-03-12  YAMAMOTO Mitsuharu  <address@hidden>
 
        * fringe.c (update_window_fringes): Remove unused variables.

=== modified file 'src/deps.mk'
--- a/src/deps.mk       2011-03-12 12:03:24 +0000
+++ b/src/deps.mk       2011-03-13 06:43:00 +0000
@@ -87,7 +87,8 @@
    msdos.h dosfns.h dispextern.h charset.h coding.h atimer.h systime.h \
    lisp.h $(config_h)
 editfns.o: editfns.c window.h buffer.h systime.h $(INTERVALS_H) character.h \
-   coding.h frame.h blockinput.h atimer.h ../lib/unistd.h ../lib/strftime.h \
+   coding.h frame.h blockinput.h atimer.h \
+   ../lib/intprops.h ../lib/strftime.h ../lib/unistd.h \
    lisp.h globals.h $(config_h)
 emacs.o: emacs.c commands.h systty.h syssignal.h blockinput.h process.h \
    termhooks.h buffer.h atimer.h systime.h $(INTERVALS_H) lisp.h $(config_h) \

=== modified file 'src/dired.c'
--- a/src/dired.c       2011-02-25 19:08:18 +0000
+++ b/src/dired.c       2011-03-11 20:24:09 +0000
@@ -848,13 +848,6 @@
   return value;
 }
 
-Lisp_Object
-make_time (time_t time)
-{
-  return Fcons (make_number (time >> 16),
-               Fcons (make_number (time & 0177777), Qnil));
-}
-
 static char *
 stat_uname (struct stat *st)
 {

=== modified file 'src/editfns.c'
--- a/src/editfns.c     2011-02-28 01:07:29 +0000
+++ b/src/editfns.c     2011-03-13 06:27:18 +0000
@@ -45,6 +45,8 @@
 #endif
 
 #include <ctype.h>
+#include <limits.h>
+#include <intprops.h>
 #include <strftime.h>
 
 #include "intervals.h"
@@ -87,6 +89,7 @@
 extern Lisp_Object w32_get_internal_run_time (void);
 #endif
 
+static void time_overflow (void) NO_RETURN;
 static int tm_diff (struct tm *, struct tm *);
 static void find_field (Lisp_Object, Lisp_Object, Lisp_Object,
                        EMACS_INT *, Lisp_Object, EMACS_INT *);
@@ -1414,6 +1417,49 @@
   return make_number (getpid ());
 }
 
+
+
+#ifndef TIME_T_MIN
+# define TIME_T_MIN TYPE_MINIMUM (time_t)
+#endif
+#ifndef TIME_T_MAX
+# define TIME_T_MAX TYPE_MAXIMUM (time_t)
+#endif
+
+/* Report that a time value is out of range for Emacs.  */
+static void
+time_overflow (void)
+{
+  error ("Specified time is not representable");
+}
+
+/* Return the upper part of the time T (everything but the bottom 16 bits),
+   making sure that it is representable.  */
+static EMACS_INT
+hi_time (time_t t)
+{
+  time_t hi = t >> 16;
+
+  /* Check for overflow, helping the compiler for common cases where
+     no runtime check is needed, and taking care not to convert
+     negative numbers to unsigned before comparing them.  */
+  if (! ((! TYPE_SIGNED (time_t)
+         || MOST_NEGATIVE_FIXNUM <= TIME_T_MIN >> 16
+         || MOST_NEGATIVE_FIXNUM <= hi)
+        && (TIME_T_MAX >> 16 <= MOST_POSITIVE_FIXNUM
+            || hi <= MOST_POSITIVE_FIXNUM)))
+    time_overflow ();
+
+  return hi;
+}
+
+/* Return the bottom 16 bits of the time T.  */
+static EMACS_INT
+lo_time (time_t t)
+{
+  return t & ((1 << 16) - 1);
+}
+
 DEFUN ("current-time", Fcurrent_time, Scurrent_time, 0, 0, 0,
        doc: /* Return the current time, as the number of seconds since 
1970-01-01 00:00:00.
 The time is returned as a list of three integers.  The first has the
@@ -1428,8 +1474,8 @@
   EMACS_TIME t;
 
   EMACS_GET_TIME (t);
-  return list3 (make_number ((EMACS_SECS (t) >> 16) & 0xffff),
-               make_number ((EMACS_SECS (t) >> 0)  & 0xffff),
+  return list3 (make_number (hi_time (EMACS_SECS (t))),
+               make_number (lo_time (EMACS_SECS (t))),
                make_number (EMACS_USECS (t)));
 }
 
@@ -1448,7 +1494,8 @@
 {
 #ifdef HAVE_GETRUSAGE
   struct rusage usage;
-  int secs, usecs;
+  time_t secs;
+  int usecs;
 
   if (getrusage (RUSAGE_SELF, &usage) < 0)
     /* This shouldn't happen.  What action is appropriate?  */
@@ -1463,8 +1510,8 @@
       secs++;
     }
 
-  return list3 (make_number ((secs >> 16) & 0xffff),
-               make_number ((secs >> 0)  & 0xffff),
+  return list3 (make_number (hi_time (secs)),
+               make_number (lo_time (secs)),
                make_number (usecs));
 #else /* ! HAVE_GETRUSAGE  */
 #ifdef WINDOWSNT
@@ -1476,6 +1523,19 @@
 }
 
 
+/* Make a Lisp list that represents the time T.  */
+Lisp_Object
+make_time (time_t t)
+{
+  return list2 (make_number (hi_time (t)),
+               make_number (lo_time (t)));
+}
+
+/* Decode a Lisp list SPECIFIED_TIME that represents a time.
+   If SPECIFIED_TIME is nil, use the current time.
+   Set *RESULT to seconds since the Epoch.
+   If USEC is not null, set *USEC to the microseconds component.
+   Return nonzero if successful.  */
 int
 lisp_time_argument (Lisp_Object specified_time, time_t *result, int *usec)
 {
@@ -1496,6 +1556,7 @@
   else
     {
       Lisp_Object high, low;
+      EMACS_INT hi;
       high = Fcar (specified_time);
       CHECK_NUMBER (high);
       low = Fcdr (specified_time);
@@ -1519,8 +1580,21 @@
       else if (usec)
         *usec = 0;
       CHECK_NUMBER (low);
-      *result = (XINT (high) << 16) + (XINT (low) & 0xffff);
-      return *result >> 16 == XINT (high);
+      hi = XINT (high);
+
+      /* Check for overflow, helping the compiler for common cases
+        where no runtime check is needed, and taking care not to
+        convert negative numbers to unsigned before comparing them.  */
+      if (! ((TYPE_SIGNED (time_t)
+             ? (TIME_T_MIN >> 16 <= MOST_NEGATIVE_FIXNUM
+                || TIME_T_MIN >> 16 <= hi)
+             : 0 <= hi)
+            && (MOST_POSITIVE_FIXNUM <= TIME_T_MAX >> 16
+                || hi <= TIME_T_MAX >> 16)))
+       return 0;
+
+      *result = (hi << 16) + (XINT (low) & 0xffff);
+      return 1;
     }
 }
 
@@ -1674,7 +1748,7 @@
   tm = ut ? gmtime (&value) : localtime (&value);
   UNBLOCK_INPUT;
   if (! tm)
-    error ("Specified time is not representable");
+    time_overflow ();
 
   synchronize_system_time_locale ();
 
@@ -1732,8 +1806,10 @@
   BLOCK_INPUT;
   decoded_time = localtime (&time_spec);
   UNBLOCK_INPUT;
-  if (! decoded_time)
-    error ("Specified time is not representable");
+  if (! (decoded_time
+        && MOST_NEGATIVE_FIXNUM - TM_YEAR_BASE <= decoded_time->tm_year
+        && decoded_time->tm_year <= MOST_POSITIVE_FIXNUM - TM_YEAR_BASE))
+    time_overflow ();
   XSETFASTINT (list_args[0], decoded_time->tm_sec);
   XSETFASTINT (list_args[1], decoded_time->tm_min);
   XSETFASTINT (list_args[2], decoded_time->tm_hour);
@@ -1757,6 +1833,20 @@
   return Flist (9, list_args);
 }
 
+/* Return OBJ - OFFSET, checking that OBJ is a valid fixnum and that
+   the result is representable as an int.  Assume OFFSET is small and
+   nonnegative.  */
+static int
+check_tm_member (Lisp_Object obj, int offset)
+{
+  EMACS_INT n;
+  CHECK_NUMBER (obj);
+  n = XINT (obj);
+  if (! (INT_MIN + offset <= n && n - offset <= INT_MAX))
+    time_overflow ();
+  return n - offset;
+}
+
 DEFUN ("encode-time", Fencode_time, Sencode_time, 6, MANY, 0,
        doc: /* Convert SECOND, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to 
internal time.
 This is the reverse operation of `decode-time', which see.
@@ -1785,19 +1875,12 @@
   struct tm tm;
   Lisp_Object zone = (nargs > 6 ? args[nargs - 1] : Qnil);
 
-  CHECK_NUMBER (args[0]);      /* second */
-  CHECK_NUMBER (args[1]);      /* minute */
-  CHECK_NUMBER (args[2]);      /* hour */
-  CHECK_NUMBER (args[3]);      /* day */
-  CHECK_NUMBER (args[4]);      /* month */
-  CHECK_NUMBER (args[5]);      /* year */
-
-  tm.tm_sec = XINT (args[0]);
-  tm.tm_min = XINT (args[1]);
-  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]) - TM_YEAR_BASE;
+  tm.tm_sec  = check_tm_member (args[0], 0);
+  tm.tm_min  = check_tm_member (args[1], 0);
+  tm.tm_hour = check_tm_member (args[2], 0);
+  tm.tm_mday = check_tm_member (args[3], 0);
+  tm.tm_mon  = check_tm_member (args[4], 1);
+  tm.tm_year = check_tm_member (args[5], TM_YEAR_BASE);
   tm.tm_isdst = -1;
 
   if (CONSP (zone))
@@ -1846,7 +1929,7 @@
     }
 
   if (time == (time_t) -1)
-    error ("Specified time is not representable");
+    time_overflow ();
 
   return make_time (time);
 }
@@ -1881,7 +1964,7 @@
   tm = localtime (&value);
   UNBLOCK_INPUT;
   if (! (tm && TM_YEAR_IN_ASCTIME_RANGE (tm->tm_year) && (tem = asctime (tm))))
-    error ("Specified time is not representable");
+    time_overflow ();
 
   /* Remove the trailing newline.  */
   tem[strlen (tem) - 1] = '\0';

=== modified file 'src/systime.h'
--- a/src/systime.h     2011-01-25 04:08:28 +0000
+++ b/src/systime.h     2011-03-11 20:24:09 +0000
@@ -144,10 +144,8 @@
    happen when this files is used outside the src directory).
    Use GCPRO1 to determine if lisp.h was included.  */
 #ifdef GCPRO1
-/* defined in dired.c */
+/* defined in editfns.c*/
 extern Lisp_Object make_time (time_t);
-
-/* defined in editfns.c*/
 extern int lisp_time_argument (Lisp_Object, time_t *, int *);
 #endif
 
@@ -172,4 +170,3 @@
 #define EMACS_TIME_LE(T1, T2) (EMACS_TIME_CMP (T1, T2) <= 0)
 
 #endif /* EMACS_SYSTIME_H */
-


reply via email to

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