Subject: Re: bin/28450: date(1) does not validate its input and accepts and processes
To: Martin Husemann , <netbsd-bugs@netbsd.org>
From: David H.Gutteridge <dhgutteridge@sympatico.ca>
List: netbsd-bugs
Date: 12/03/2004 00:55:24
Here's a patch which prints out the date it received, as suggested.  It also processes the entire date string rather than "erroring out" at the first problem it encounters, so it will identify anything it finds amiss (I think...).

It occured to me, for consistency of visual presentation, it might be better to check for problems with seconds after everything else (matching the input order), but I've left it as-is, it's an easy swap.

--- date.c.orig	Thu Aug  7 09:05:09 2003
+++ date.c	Fri Dec  3 00:31:07 2004
@@ -68,6 +68,7 @@
 int main(int, char *[]);
 static void badformat(void);
 static void badtime(void);
+static void badvalue(const char *, const struct tm *);
 static void setthetime(const char *);
 static void usage(void);
 
@@ -138,6 +139,18 @@
 	/* NOTREACHED */
 }
 
+static void
+badvalue(const char *param, const struct tm *lt)
+{
+	const int PAROFFSET = 2;	/* offset from initial comma */
+
+	warnx("invalid %s supplied", param + PAROFFSET);
+	warnx("date supplied was: %.4d/%.2d/%.2d %.2d:%.2d:%.2d",
+		lt->tm_year + TM_YEAR_BASE, lt->tm_mon + 1, lt->tm_mday,
+		lt->tm_hour, lt->tm_min, lt->tm_sec);
+	usage();
+}
+
 #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
 
 static void
@@ -147,6 +160,10 @@
 	struct tm *lt;
 	const char *dot, *t;
 	int len, yearset;
+	const int VALERRSZ = 60;
+	char valerrs[VALERRSZ];
+
+	valerrs[0] = '\0';
 
 	for (t = p, dot = NULL; *t; ++t) {
 		if (isdigit((unsigned char)*t))
@@ -168,6 +185,8 @@
 			badformat();
 		++dot;
 		lt->tm_sec = ATOI2(dot);
+		if (lt->tm_sec > 61)
+			strncat(valerrs, ", seconds", VALERRSZ - 1);
 	} else {
 		len = 0;
 		lt->tm_sec = 0;
@@ -177,6 +196,8 @@
 	switch (strlen(p) - len) {
 	case 12:				/* cc */
 		lt->tm_year = ATOI2(p) * 100 - TM_YEAR_BASE;
+		if (lt->tm_year < 0)
+			badtime();
 		yearset = 1;
 		/* FALLTHROUGH */
 	case 10:				/* yy */
@@ -192,20 +213,58 @@
 		/* FALLTHROUGH */
 	case 8:					/* mm */
 		lt->tm_mon = ATOI2(p);
+		if (lt->tm_mon > 12 || lt->tm_mon == 0)
+			strncat(valerrs, ", month", VALERRSZ - 1);
 		--lt->tm_mon;			/* time struct is 0 - 11 */
 		/* FALLTHROUGH */
 	case 6:					/* dd */
 		lt->tm_mday = ATOI2(p);
+		switch (lt->tm_mon) {
+		case 0:
+		case 2:
+		case 4:
+		case 6:
+		case 7:
+		case 9:
+		case 11:
+			if (lt->tm_mday > 31 || lt->tm_mday == 0)
+				strncat(valerrs, ", day of month", VALERRSZ - 1);
+			break;
+		case 3:
+		case 5:
+		case 8:
+		case 10:
+			if (lt->tm_mday > 30 || lt->tm_mday == 0)
+				strncat(valerrs, ", day of month", VALERRSZ - 1);
+			break;
+		case 1:
+			if (lt->tm_mday > 29 || lt->tm_mday == 0 ||
+			    (lt->tm_mday == 29 &&
+			     !isleap(lt->tm_year + TM_YEAR_BASE)))
+				strncat(valerrs, ", day of month", VALERRSZ - 1);
+			break;
+		default:
+			strncat(valerrs, ", day of month", VALERRSZ - 1);
+			break;
+		}
 		/* FALLTHROUGH */
 	case 4:					/* hh */
 		lt->tm_hour = ATOI2(p);
+		if (lt->tm_hour > 23)
+			strncat(valerrs, ", hour", VALERRSZ - 1);
 		/* FALLTHROUGH */
 	case 2:					/* mm */
 		lt->tm_min = ATOI2(p);
+		if (lt->tm_min > 59)
+			strncat(valerrs, ", minute", VALERRSZ - 1);
 		break;
 	default:
 		badformat();
 	}
+
+	/* deal with any value errors */
+	if (valerrs[0] != '\0')
+		badvalue(valerrs, lt);
 
 	/* convert broken-down time to UTC clock time */
 	if ((tval = mktime(lt)) == -1)


> From: Martin Husemann <martin@duskware.de>
> Date: 2004/11/29 Mon AM 03:35:40 EST
> To: dhgutteridge@sympatico.ca
> CC: gnats-admin@netbsd.org,  netbsd-bugs@netbsd.org
> Subject: Re: bin/28450: date(1) does not validate its input and accepts and processes impossible dates
> 
> On Mon, Nov 29, 2004 at 03:45:00AM +0000, dhgutteridge@sympatico.ca wrote:
> > I have supplied a proposed patch which
> 
> You know, if you reach a finger we'll take your arm...
> 
> I like this, but I think it might make sense to have badvalue() print the
> "decoded" date string, as the format is .. not exactly human friendly,
> given how many variations of the order exists and the many optional parts...
> 
> While there, could you check other commands that take a date string?
> at(1), cal(1), probably others.
> 
> Martin
>