shithub: scc

Download patch

ref: 1c8ba5d798e7676ca4240e73db7fc36cbcfecdbb
parent: fe02e90431559e9440f10983457eb01eece016c0
author: Naveen Narayanan <[email protected]>
date: Tue Sep 29 13:43:17 EDT 2020

libc: Fix bug in mktime.c

norm() decrements tm_min(tm_hour, tm_mday, tm_year) if there is an
overshoot of tm_sec(tm_min,tm_hour,tm_mon) instead of respectively
incrementing tm_min(tm_hour, tm_mday, tm_year).
norm() increments tm_min(tm_hour, tm_mday, tm_year) if there is an
undershoot of tm_sec(tm_min,tm_hour,tm_mon) instead of respectively
decrementing tm_min(tm_hour, tm_mday, tm_year).

normalize() doesn't set the day of the month appropriately in the case
of day < 1 as it doesn't decrement mon prior to that. This happens
when there is an undershoot of tm_sec through tm_hour.

You can reproduce the bug by setting struct tm to the following and
calling mktime:

tim.tm_hour = -2;
tim.tm_mday = 1;
tim.tm_mon = 2;
tim.tm_year = 120;
tim.tm_isdst = 0;

t = mktime(&tim);
assert(t != -1);
assert(tim.tm_hour == 22);
assert(tim.tm_mday == 29);
assert(tim.tm_mon == 1);

--- a/src/libc/time/mktime.c
+++ b/src/libc/time/mktime.c
@@ -14,7 +14,7 @@
 		v += d * qty;
 		if (n > INT_MAX - d)
 			return 0;
-		n += d;
+		n -= d;
 	}
 	if (v >= qty) {
 		d = v / qty;
@@ -21,7 +21,7 @@
 		v -= d * qty;
 		if (n < INT_MIN + d)
 			return 0;
-		n -= d;
+		n += d;
 	}
 
 	*val = v;
@@ -48,7 +48,6 @@
 	_daysmon[FEB] = FEBDAYS(year);
 
 	for (mon = tm->tm_mon; day < 1; --mon) {
-		day += _daysmon[mon];
 		if (mon == JAN) {
 			if (year == EPOCH)
 				return 0;
@@ -56,6 +55,7 @@
 			_daysmon[FEB] = FEBDAYS(year);
 			mon = DEC+1;
 		}
+		day += _daysmon[mon-1];
 	}
 
 	for (; day > _daysmon[mon]; ++mon) {