From ec2215726cffb976019d08ebf569edd2229e9dba Mon Sep 17 00:00:00 2001
From: Matt Johnston <matt@ucc.asn.au>
Date: Thu, 1 Dec 2022 11:34:43 +0800
Subject: Fix y2038 issues with time_t conversion

These changes were identified by building with and without
-D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64
on 32-bit arm, logging warnings to files.
-Wconversion was added to CFLAGS in both builds.

Then a "diff -I Wconversion log1 log2" shows new warnings that appear
with the 64-bit time_t. There are a few false positives that have been
fixed for quietness.

struct logininfo and struct wtmp are still problematic, those will
need to be handled by libc.
---
 common-session.c | 43 +++++++++++++++++++++++++++----------------
 dbutil.c         |  2 +-
 loginrec.c       |  2 ++
 loginrec.h       |  4 ++--
 runopts.h        |  4 ++--
 svr-auth.c       |  2 +-
 6 files changed, 35 insertions(+), 22 deletions(-)

--- a/common-session.c
+++ b/common-session.c
@@ -519,15 +519,24 @@ static void send_msg_keepalive() {
 	ses.last_packet_time_idle = old_time_idle;
 }
 
+/* Returns the difference in seconds, clamped to LONG_MAX */
+static long elapsed(time_t now, time_t prev) {
+	time_t del = now - prev;
+	if (del > LONG_MAX) {
+		return LONG_MAX;
+	}
+	return (long)del;
+}
+
 /* Check all timeouts which are required. Currently these are the time for
  * user authentication, and the automatic rekeying. */
 static void checktimeouts() {
 
 	time_t now;
 	now = monotonic_now();
-	
+
 	if (IS_DROPBEAR_SERVER && ses.connect_time != 0
-		&& now - ses.connect_time >= AUTH_TIMEOUT) {
+		&& elapsed(now, ses.connect_time) >= AUTH_TIMEOUT) {
 			dropbear_close("Timeout before auth");
 	}
 
@@ -537,45 +546,47 @@ static void checktimeouts() {
 	}
 
 	if (!ses.kexstate.sentkexinit
-			&& (now - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
+			&& (elapsed(now, ses.kexstate.lastkextime) >= KEX_REKEY_TIMEOUT
 			|| ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)) {
 		TRACE(("rekeying after timeout or max data reached"))
 		send_msg_kexinit();
 	}
-	
+
 	if (opts.keepalive_secs > 0 && ses.authstate.authdone) {
 		/* Avoid sending keepalives prior to auth - those are
 		not valid pre-auth packet types */
 
 		/* Send keepalives if we've been idle */
-		if (now - ses.last_packet_time_any_sent >= opts.keepalive_secs) {
+		if (elapsed(now, ses.last_packet_time_any_sent) >= opts.keepalive_secs) {
 			send_msg_keepalive();
 		}
 
 		/* Also send an explicit keepalive message to trigger a response
 		if the remote end hasn't sent us anything */
-		if (now - ses.last_packet_time_keepalive_recv >= opts.keepalive_secs
-			&& now - ses.last_packet_time_keepalive_sent >= opts.keepalive_secs) {
+		if (elapsed(now, ses.last_packet_time_keepalive_recv) >= opts.keepalive_secs
+			&& elapsed(now, ses.last_packet_time_keepalive_sent) >= opts.keepalive_secs) {
 			send_msg_keepalive();
 		}
 
-		if (now - ses.last_packet_time_keepalive_recv 
+		if (elapsed(now, ses.last_packet_time_keepalive_recv)
 			>= opts.keepalive_secs * DEFAULT_KEEPALIVE_LIMIT) {
 			dropbear_exit("Keepalive timeout");
 		}
 	}
 
-	if (opts.idle_timeout_secs > 0 
-			&& now - ses.last_packet_time_idle >= opts.idle_timeout_secs) {
+	if (opts.idle_timeout_secs > 0
+			&& elapsed(now, ses.last_packet_time_idle) >= opts.idle_timeout_secs) {
 		dropbear_close("Idle timeout");
 	}
 }
 
-static void update_timeout(long limit, long now, long last_event, long * timeout) {
-	TRACE2(("update_timeout limit %ld, now %ld, last %ld, timeout %ld",
-		limit, now, last_event, *timeout))
+static void update_timeout(long limit, time_t now, time_t last_event, long * timeout) {
+	TRACE2(("update_timeout limit %ld, now %llu, last %llu, timeout %ld",
+		limit,
+		(unsigned long long)now,
+		(unsigned long long)last_event, *timeout))
 	if (last_event > 0 && limit > 0) {
-		*timeout = MIN(*timeout, last_event+limit-now);
+		*timeout = MIN(*timeout, elapsed(now, last_event) + limit);
 		TRACE2(("new timeout %ld", *timeout))
 	}
 }
@@ -584,7 +595,7 @@ static long select_timeout() {
 	/* determine the minimum timeout that might be required, so
 	as to avoid waking when unneccessary */
 	long timeout = KEX_REKEY_TIMEOUT;
-	long now = monotonic_now();
+	time_t now = monotonic_now();
 
 	if (!ses.kexstate.sentkexinit) {
 		update_timeout(KEX_REKEY_TIMEOUT, now, ses.kexstate.lastkextime, &timeout);
@@ -596,7 +607,7 @@ static long select_timeout() {
 	}
 
 	if (ses.authstate.authdone) {
-		update_timeout(opts.keepalive_secs, now, 
+		update_timeout(opts.keepalive_secs, now,
 			MAX(ses.last_packet_time_keepalive_recv, ses.last_packet_time_keepalive_sent),
 			&timeout);
 	}
--- a/dbutil.c
+++ b/dbutil.c
@@ -724,7 +724,7 @@ void gettime_wrapper(struct timespec *no
 	/* Fallback for everything else - this will sometimes go backwards */
 	gettimeofday(&tv, NULL);
 	now->tv_sec = tv.tv_sec;
-	now->tv_nsec = 1000*tv.tv_usec;
+	now->tv_nsec = 1000*(long)tv.tv_usec;
 }
 
 /* second-resolution monotonic timestamp */
--- a/loginrec.c
+++ b/loginrec.c
@@ -459,6 +459,7 @@ line_abbrevname(char *dst, const char *s
 void
 set_utmp_time(struct logininfo *li, struct utmp *ut)
 {
+	/* struct utmp in glibc isn't y2038 safe yet */
 # ifdef HAVE_STRUCT_UTMP_UT_TV
 	ut->ut_tv.tv_sec = li->tv_sec;
 	ut->ut_tv.tv_usec = li->tv_usec;
@@ -1272,6 +1273,7 @@ lastlog_construct(struct logininfo *li,
 	(void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line));
 	strlcpy(last->ll_host, li->hostname,
 		MIN_SIZEOF(last->ll_host, li->hostname));
+	/* struct lastlog in glibc isn't y2038 safe yet */
 	last->ll_time = li->tv_sec;
 }
 
--- a/loginrec.h
+++ b/loginrec.h
@@ -139,8 +139,8 @@ struct logininfo {
 	/* struct timeval (sys/time.h) isn't always available, if it isn't we'll
 	 * use time_t's value as tv_sec and set tv_usec to 0
 	 */
-	unsigned int tv_sec;
-	unsigned int tv_usec;
+	time_t tv_sec;
+	suseconds_t tv_usec;
 	union login_netinfo hostaddr;       /* caller's host address(es) */
 }; /* struct logininfo */
 
--- a/runopts.h
+++ b/runopts.h
@@ -39,8 +39,8 @@ typedef struct runopts {
 	int listen_fwd_all;
 #endif
 	unsigned int recv_window;
-	time_t keepalive_secs; /* Time between sending keepalives. 0 is off */
-	time_t idle_timeout_secs; /* Exit if no traffic is sent/received in this time */
+	long keepalive_secs; /* Time between sending keepalives. 0 is off */
+	long idle_timeout_secs; /* Exit if no traffic is sent/received in this time */
 	int usingsyslog;
 
 #ifndef DISABLE_ZLIB
--- a/svr-auth.c
+++ b/svr-auth.c
@@ -389,7 +389,7 @@ void send_msg_userauth_failure(int parti
 		Beware of integer overflow if increasing these values */
 		const unsigned int mindelay = 250000000;
 		const unsigned int vardelay = 100000000;
-		unsigned int rand_delay;
+		suseconds_t rand_delay;
 		struct timespec delay;
 
 		gettime_wrapper(&delay);
