From 37a4d178474e3b85faede06e44abda4d0b2a4da0 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Wed, 1 Apr 2026 00:35:13 -0700
Subject: [PROPOSED] Fix zic overflow bug with too-large offsets
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Without this fix, ‘Zone Ouch 2147483648:00:00 - LMT’ causes zic
to convert 2147483648 to int, which can cause a trap.
Other adversarial inputs can even cause buffer overflow.
Problem reported by Naveed Khan in:
https://lists.iana.org/hyperkitty/list/tz@iana.org/thread/POOFMB4SHW2U6BNNNQVGH6ZK7TLBLK7P/
* NEWS: Mention this.
* zic.c (stringoffset): Check for too-large hour offset before
assigning it to an int.
---
 NEWS  | 7 +++++--
 zic.c | 4 ++--
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/NEWS b/NEWS
index a297861d..1a97cbfe 100644
--- a/NEWS
+++ b/NEWS
@@ -21,8 +21,11 @@ Unreleased, experimental changes
 
     zic no longer overflows a buffer when generating a TZ string like
     "PST-167:59:58PDT-167:59:59,M11.5.6/-167:59:59,M12.5.6/-167:59:59",
-    which can occur with adversarial input.  (Thanks to GitHub
-    user rootvector2.)
+    which can occur with adversarial input.  (Thanks to Naveed Khan.)
+
+    zic no longer overflows an int when processing input like ‘Zone
+    Ouch 2147483648:00:00 - LMT’.  The int overflow can lead to buffer
+    overflow in adversarial cases.  (Thanks to Naveed Khan.)
 
     zic now checks for signals more often.
 
diff --git a/zic.c b/zic.c
index c0891001..6e859d09 100644
--- a/zic.c
+++ b/zic.c
@@ -3182,11 +3182,11 @@ stringoffset(char *result, zic_t offset)
 	offset /= SECSPERMIN;
 	minutes = offset % MINSPERHOUR;
 	offset /= MINSPERHOUR;
-	hours = offset;
-	if (hours >= HOURSPERDAY * DAYSPERWEEK) {
+	if (offset >= HOURSPERDAY * DAYSPERWEEK) {
 		result[0] = '\0';
 		return 0;
 	}
+	hours = offset;
 	len += sprintf(result + len, "%d", hours);
 	if (minutes != 0 || seconds != 0) {
 		len += sprintf(result + len, ":%02d", minutes);
-- 
2.51.0

