Module Name:    src
Committed By:   kre
Date:           Thu Feb  8 02:54:07 UTC 2024

Modified Files:
        src/usr.bin/touch: touch.c

Log Message:
Check that mktime() (or timegm() the one time it is used) do not
alter any of the material fields of the struct tm that was handed
to it.   If any were altered, then the time string passed in was
not a valid time representation, and so should be rejected.

This one is not an invisible change, it prevents use of things like
"-t 202402300000"  (which previously would have been interpreted as
"-t 202403010000" - the day after the 29th of Feb in 2024).

I believe this is an improvement however, and in line with the
general intent that if you specify a date and time, that exact
date and time is what touch should be using.   It does mean that
specifying "60" for the seconds field is almost guaranteed to
fail on any POSIX system, as leap seconds simply don't exist
there (on a non-POSIX-conforming system that uses leap seconds,
the :60 should work, if specified with the correct date and time
at which the leap second actually occurs).

The one exception is when parsedate(3) is used, as that does not
do this check (which allows things like "-1 day" on the 1st of
a month to work).

(This is the last of this sequence of updates to touch.c, an
update to touch.1 follows).


To generate a diff of this commit:
cvs rdiff -u -r1.37 -r1.38 src/usr.bin/touch/touch.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/usr.bin/touch/touch.c
diff -u src/usr.bin/touch/touch.c:1.37 src/usr.bin/touch/touch.c:1.38
--- src/usr.bin/touch/touch.c:1.37	Thu Feb  8 02:53:53 2024
+++ src/usr.bin/touch/touch.c	Thu Feb  8 02:54:07 2024
@@ -1,4 +1,4 @@
-/*	$NetBSD: touch.c,v 1.37 2024/02/08 02:53:53 kre Exp $	*/
+/*	$NetBSD: touch.c,v 1.38 2024/02/08 02:54:07 kre Exp $	*/
 
 /*
  * Copyright (c) 1993
@@ -39,7 +39,7 @@ __COPYRIGHT("@(#) Copyright (c) 1993\
 #if 0
 static char sccsid[] = "@(#)touch.c	8.2 (Berkeley) 4/28/95";
 #endif
-__RCSID("$NetBSD: touch.c,v 1.37 2024/02/08 02:53:53 kre Exp $");
+__RCSID("$NetBSD: touch.c,v 1.38 2024/02/08 02:54:07 kre Exp $");
 #endif /* not lint */
 
 #include <sys/types.h>
@@ -67,6 +67,7 @@ static void	stime_arg1(char *, struct ti
 static void	stime_arg2(const char *, int, struct timespec *);
 static void	stime_file(const char *, struct timespec *);
 static int	stime_posix(const char *, struct timespec *);
+static int	difftm(const struct tm *, const struct tm *);
 __dead static void	usage(void);
 
 struct option touch_longopts[] = {
@@ -233,7 +234,7 @@ stime_arg0(const char *arg, struct times
 static void
 stime_arg1(char *arg, struct timespec *tsp)
 {
-	struct tm *t;
+	struct tm *t, tm;
 	time_t tmptime;
 	int yearset;
 	char *p;
@@ -290,8 +291,9 @@ stime_arg1(char *arg, struct timespec *t
 	}
 
 	t->tm_isdst = -1;		/* Figure out DST. */
+	tm = *t;
 	tsp[0].tv_sec = tsp[1].tv_sec = mktime(t);
-	if (tsp[0].tv_sec == NO_TIME)
+	if (tsp[0].tv_sec == NO_TIME || difftm(t, &tm))
  terr:		errx(EXIT_FAILURE, "out of range or bad time specification:\n"
 		    "\t'%s' should be [[CC]YY]MMDDhhmm[.ss]", initarg);
 
@@ -301,7 +303,7 @@ stime_arg1(char *arg, struct timespec *t
 static void
 stime_arg2(const char *arg, int year, struct timespec *tsp)
 {
-	struct tm *t;
+	struct tm *t, tm;
 	time_t tmptime;
 					/* Start with the current time. */
 	tmptime = tsp[0].tv_sec;
@@ -323,8 +325,9 @@ stime_arg2(const char *arg, int year, st
 	t->tm_sec = 0;
 
 	t->tm_isdst = -1;		/* Figure out DST. */
+	tm = *t;
 	tsp[0].tv_sec = tsp[1].tv_sec = mktime(t);
-	if (tsp[0].tv_sec == NO_TIME)
+	if (tsp[0].tv_sec == NO_TIME || difftm(t, &tm))
 		errx(EXIT_FAILURE,
 		    "out of range or bad time specification: MMDDhhmm[YY]");
 
@@ -345,7 +348,7 @@ stime_file(const char *fname, struct tim
 static int
 stime_posix(const char *arg, struct timespec *tsp)
 {
-	struct tm tm;
+	struct tm tm, tms;
 	const char *p;
 	char *ep;
 	int utc = 0;
@@ -361,6 +364,8 @@ stime_posix(const char *arg, struct time
 	if (!isdigch(arg[0]))	/* and the first must be a digit! */
 		return 0;
 
+	(void)memset(&tm, 0, sizeof tm);
+
 	errno = 0;
 	val = strtol(arg, &ep, 10);		/* YYYY */
 	if (val < 0 || val > INT_MAX)
@@ -472,17 +477,46 @@ stime_posix(const char *arg, struct time
 		return 0;
 
 	tm.tm_isdst = -1;
+	tms = tm;
 	if (utc)
 		tsp[0].tv_sec = tsp[1].tv_sec = timegm(&tm);
 	else
 		tsp[0].tv_sec = tsp[1].tv_sec = mktime(&tm);
 
-	if (errno != 0 && tsp[1].tv_sec == NO_TIME)
+	if ((errno != 0 && tsp[1].tv_sec == NO_TIME) || difftm(&tm, &tms))
 		return 0;
 
 	return 1;
 }
 
+/*
+ * Determine whether 2 struct tn's are different
+ * return true (1) if theu are, false (0) otherwise.
+ *
+ * Note that we only consider the fields that are set
+ * for mktime() to use - if mktime() returns them
+ * differently than was set, then there was a problem
+ * with the setting.
+ */
+static int
+difftm(const struct tm *t1, const struct tm *t2)
+{
+#define CHK(fld) do {						\
+			if (t1->tm_##fld != t2->tm_##fld) {	\
+				return  1;			\
+			}					\
+		} while(/*CONSTCOND*/0)
+
+	CHK(year);
+	CHK(mon);
+	CHK(mday);
+	CHK(hour);
+	CHK(min);
+	CHK(sec);
+
+	return 0;
+}
+
 static void
 usage(void)
 {

Reply via email to