Improve quality of tests for time stamp overflow.
This commit is contained in:
commit
d32df629a2
5 changed files with 143 additions and 37 deletions
|
@ -1,3 +1,35 @@
|
|||
2011-03-13 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 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 <mituharu@math.s.chiba-u.ac.jp>
|
||||
|
||||
* fringe.c (update_window_fringes): Remove unused variables.
|
||||
|
|
|
@ -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 \
|
||||
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) \
|
||||
|
|
|
@ -848,13 +848,6 @@ file_name_completion_stat (Lisp_Object dirname, DIRENTRY *dp, struct stat *st_ad
|
|||
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)
|
||||
{
|
||||
|
|
133
src/editfns.c
133
src/editfns.c
|
@ -45,6 +45,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
|
|||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <intprops.h>
|
||||
#include <strftime.h>
|
||||
|
||||
#include "intervals.h"
|
||||
|
@ -87,6 +89,7 @@ extern char **environ;
|
|||
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 @@ DEFUN ("emacs-pid", Femacs_pid, Semacs_pid, 0, 0, 0,
|
|||
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 @@ resolution finer than a second. */)
|
|||
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 @@ on systems that do not provide resolution finer than a second. */)
|
|||
{
|
||||
#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 @@ on systems that do not provide resolution finer than a second. */)
|
|||
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 @@ on systems that do not provide resolution finer than a second. */)
|
|||
}
|
||||
|
||||
|
||||
/* 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 @@ lisp_time_argument (Lisp_Object specified_time, time_t *result, int *usec)
|
|||
else
|
||||
{
|
||||
Lisp_Object high, low;
|
||||
EMACS_INT hi;
|
||||
high = Fcar (specified_time);
|
||||
CHECK_NUMBER (high);
|
||||
low = Fcdr (specified_time);
|
||||
|
@ -1519,8 +1580,21 @@ lisp_time_argument (Lisp_Object specified_time, time_t *result, int *usec)
|
|||
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 @@ For example, to produce full ISO 8601 format, use "%Y-%m-%dT%T%z". */)
|
|||
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 @@ DOW and ZONE.) */)
|
|||
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 @@ DOW and ZONE.) */)
|
|||
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 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */)
|
|||
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 @@ usage: (encode-time SECOND MINUTE HOUR DAY MONTH YEAR &optional ZONE) */)
|
|||
}
|
||||
|
||||
if (time == (time_t) -1)
|
||||
error ("Specified time is not representable");
|
||||
time_overflow ();
|
||||
|
||||
return make_time (time);
|
||||
}
|
||||
|
@ -1881,7 +1964,7 @@ but this is considered obsolete. */)
|
|||
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';
|
||||
|
|
|
@ -144,10 +144,8 @@ extern void set_waiting_for_input (EMACS_TIME *);
|
|||
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 */
|
||||
extern Lisp_Object make_time (time_t);
|
||||
|
||||
/* defined in editfns.c*/
|
||||
extern Lisp_Object make_time (time_t);
|
||||
extern int lisp_time_argument (Lisp_Object, time_t *, int *);
|
||||
#endif
|
||||
|
||||
|
@ -172,4 +170,3 @@ extern int lisp_time_argument (Lisp_Object, time_t *, int *);
|
|||
#define EMACS_TIME_LE(T1, T2) (EMACS_TIME_CMP (T1, T2) <= 0)
|
||||
|
||||
#endif /* EMACS_SYSTIME_H */
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue