Commit b8d9bd41 authored by Paul Eggert's avatar Paul Eggert
Browse files

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 it, it reports time overflow.
* 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.
parent fe31d94c
2011-03-12 Paul Eggert <eggert@cs.ucla.edu>
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 it, it reports time overflow.
* 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.
2011-03-11 Paul Eggert <eggert@cs.ucla.edu> 2011-03-11 Paul Eggert <eggert@cs.ucla.edu>
   
* editfns.c (time_overflow): New function, refactoring common code. * editfns.c (time_overflow): New function, refactoring common code.
...@@ -87,7 +87,8 @@ dosfns.o: buffer.h termchar.h termhooks.h frame.h blockinput.h window.h \ ...@@ -87,7 +87,8 @@ dosfns.o: buffer.h termchar.h termhooks.h frame.h blockinput.h window.h \
msdos.h dosfns.h dispextern.h charset.h coding.h atimer.h systime.h \ msdos.h dosfns.h dispextern.h charset.h coding.h atimer.h systime.h \
lisp.h $(config_h) lisp.h $(config_h)
editfns.o: editfns.c window.h buffer.h systime.h $(INTERVALS_H) character.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) lisp.h globals.h $(config_h)
emacs.o: emacs.c commands.h systty.h syssignal.h blockinput.h process.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) \ termhooks.h buffer.h atimer.h systime.h $(INTERVALS_H) lisp.h $(config_h) \
......
...@@ -45,6 +45,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ ...@@ -45,6 +45,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#endif #endif
#include <ctype.h> #include <ctype.h>
#include <limits.h>
#include <intprops.h>
#include <strftime.h> #include <strftime.h>
#include "intervals.h" #include "intervals.h"
...@@ -1415,6 +1417,44 @@ DEFUN ("emacs-pid", Femacs_pid, Semacs_pid, 0, 0, 0, ...@@ -1415,6 +1417,44 @@ DEFUN ("emacs-pid", Femacs_pid, Semacs_pid, 0, 0, 0,
return make_number (getpid ()); 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;
if (((TYPE_SIGNED (time_t)
&& TIME_T_MIN >> 16 < MOST_NEGATIVE_FIXNUM
&& hi < MOST_NEGATIVE_FIXNUM)
|| (MOST_POSITIVE_FIXNUM < TIME_T_MAX >> 16
&& MOST_POSITIVE_FIXNUM < hi)))
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, 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. 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 The time is returned as a list of three integers. The first has the
...@@ -1429,8 +1469,8 @@ resolution finer than a second. */) ...@@ -1429,8 +1469,8 @@ resolution finer than a second. */)
EMACS_TIME t; EMACS_TIME t;
EMACS_GET_TIME (t); EMACS_GET_TIME (t);
return list3 (make_number ((EMACS_SECS (t) >> 16) & 0xffff), return list3 (make_number (hi_time (EMACS_SECS (t))),
make_number ((EMACS_SECS (t) >> 0) & 0xffff), make_number (lo_time (EMACS_SECS (t))),
make_number (EMACS_USECS (t))); make_number (EMACS_USECS (t)));
} }
...@@ -1449,7 +1489,8 @@ on systems that do not provide resolution finer than a second. */) ...@@ -1449,7 +1489,8 @@ on systems that do not provide resolution finer than a second. */)
{ {
#ifdef HAVE_GETRUSAGE #ifdef HAVE_GETRUSAGE
struct rusage usage; struct rusage usage;
int secs, usecs; time_t secs;
int usecs;
if (getrusage (RUSAGE_SELF, &usage) < 0) if (getrusage (RUSAGE_SELF, &usage) < 0)
/* This shouldn't happen. What action is appropriate? */ /* This shouldn't happen. What action is appropriate? */
...@@ -1464,8 +1505,8 @@ on systems that do not provide resolution finer than a second. */) ...@@ -1464,8 +1505,8 @@ on systems that do not provide resolution finer than a second. */)
secs++; secs++;
} }
return list3 (make_number ((secs >> 16) & 0xffff), return list3 (make_number (hi_time (secs)),
make_number ((secs >> 0) & 0xffff), make_number (lo_time (secs)),
make_number (usecs)); make_number (usecs));
#else /* ! HAVE_GETRUSAGE */ #else /* ! HAVE_GETRUSAGE */
#ifdef WINDOWSNT #ifdef WINDOWSNT
...@@ -1477,19 +1518,12 @@ on systems that do not provide resolution finer than a second. */) ...@@ -1477,19 +1518,12 @@ on systems that do not provide resolution finer than a second. */)
} }
/* Report a time value that is out of range for Emacs. */
static void
time_overflow (void)
{
error ("Specified time is not representable");
}
/* Make a Lisp list that represents the time T. */ /* Make a Lisp list that represents the time T. */
Lisp_Object Lisp_Object
make_time (time_t t) make_time (time_t t)
{ {
return Fcons (make_number (t >> 16), return list2 (make_number (hi_time (t)),
Fcons (make_number (t & 0177777), Qnil)); make_number (lo_time (t)));
} }
/* Decode a Lisp list SPECIFIED_TIME that represents a time. /* Decode a Lisp list SPECIFIED_TIME that represents a time.
...@@ -1753,7 +1787,9 @@ DOW and ZONE.) */) ...@@ -1753,7 +1787,9 @@ DOW and ZONE.) */)
BLOCK_INPUT; BLOCK_INPUT;
decoded_time = localtime (&time_spec); decoded_time = localtime (&time_spec);
UNBLOCK_INPUT; UNBLOCK_INPUT;
if (! decoded_time) 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 (); time_overflow ();
XSETFASTINT (list_args[0], decoded_time->tm_sec); XSETFASTINT (list_args[0], decoded_time->tm_sec);
XSETFASTINT (list_args[1], decoded_time->tm_min); XSETFASTINT (list_args[1], decoded_time->tm_min);
...@@ -1778,6 +1814,20 @@ DOW and ZONE.) */) ...@@ -1778,6 +1814,20 @@ DOW and ZONE.) */)
return Flist (9, list_args); 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, DEFUN ("encode-time", Fencode_time, Sencode_time, 6, MANY, 0,
doc: /* Convert SECOND, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to internal time. doc: /* Convert SECOND, MINUTE, HOUR, DAY, MONTH, YEAR and ZONE to internal time.
This is the reverse operation of `decode-time', which see. This is the reverse operation of `decode-time', which see.
...@@ -1806,19 +1856,12 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */) ...@@ -1806,19 +1856,12 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */)
struct tm tm; struct tm tm;
Lisp_Object zone = (nargs > 6 ? args[nargs - 1] : Qnil); Lisp_Object zone = (nargs > 6 ? args[nargs - 1] : Qnil);
CHECK_NUMBER (args[0]); /* second */ tm.tm_sec = check_tm_member (args[0], 0);
CHECK_NUMBER (args[1]); /* minute */ tm.tm_min = check_tm_member (args[1], 0);
CHECK_NUMBER (args[2]); /* hour */ tm.tm_hour = check_tm_member (args[2], 0);
CHECK_NUMBER (args[3]); /* day */ tm.tm_mday = check_tm_member (args[3], 0);
CHECK_NUMBER (args[4]); /* month */ tm.tm_mon = check_tm_member (args[4], 1);
CHECK_NUMBER (args[5]); /* year */ tm.tm_year = check_tm_member (args[5], TM_YEAR_BASE);
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_isdst = -1; tm.tm_isdst = -1;
if (CONSP (zone)) if (CONSP (zone))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment