Problems reported by Naveed Khan.
* NEWS: Mention this.
* zic.c (gethms): Restrict output range to a quarter of
the machine range.
(outzone): Remove no-longer-needed integer overflow checks.
---
NEWS | 10 ++++++----
zic.c | 32 +++++++++++++++++++++-----------
2 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/NEWS b/NEWS
index 042602c3..07e5fc69 100644
--- a/NEWS
+++ b/NEWS
@@ -4,7 +4,7 @@ Unreleased, experimental changes
Briefly:
Alberta moved to permanent -06 on 2026-06-18.
- Another integer overflow bug has been fixed in zic.
+ More integer overflow bugs have been fixed in zic.
Changes to future timestamps
@@ -18,9 +18,11 @@ Unreleased, experimental changes
Changes to code
- zic no longer overflows an integer when processing outlandish
- input like ‘Zone Ouch 0 - LMT 9223372036854775807’. The integer
- overflow is undefined behavior in C. (Thanks to Naveed Khan.)
+ zic no longer overflows integers when processing outlandish input
+ like ‘Zone Ouch 0 - LMT 9223372036854775807’, ‘Zone Ouch 0
+ 2562047788015215 LMT’, ‘Zone Ouch -2562047788015215:30:08 - LMT’,
+ and ‘Zone Ouch -2562047788015215:30:08 - %%z’. This avoids
+ undefined behavior in C. (Problems reported by Naveed Khan.)
Changes to commentary
diff --git a/zic.c b/zic.c
index 1115e9ba..248e4f0f 100644
--- a/zic.c
+++ b/zic.c
@@ -2060,7 +2060,7 @@ infile(int fnum, char const *name)
static zic_t
gethms(char const *string, char const *errstring)
{
- zic_t hh;
+ zic_t hh, r;
int sign, mm = 0, ss = 0;
char hhx, mmx, ssx, xr = '0', xs;
int tenths = 0;
@@ -2103,8 +2103,19 @@ gethms(char const *string, char const *errstring)
if (noise && (hh > HOURSPERDAY ||
(hh == HOURSPERDAY && (mm != 0 || ss != 0))))
warning(_("values over 24 hours not handled by pre-2007 versions of zic"));
- return oadd(omul(hh, sign * SECSPERHOUR),
- sign * (mm * SECSPERMIN + ss));
+ r = oadd(omul(hh, sign * SECSPERHOUR), sign * (mm * SECSPERMIN + ss));
+
+ /* Check that 4 * R fits in zic_t. This is more than enough
+ for times of day and for UT offsets in TZif files, and it
+ avoids later problems during arithmetic on these numbers,
+ as stringzone can compute up to 3 times one of these values
+ when computing fake timezones, and stringrule can do a bit more.
+ Do not limit R strictly to 32 bits here, as it is OK to go
+ over the TZif limit temporarily so long as the result fits. */
+ omul(r, 4);
+ /* Discard omul's return value, as it is not needed here. */
+
+ return r;
}
static zic_t
@@ -3528,7 +3539,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
int type;
save = zp->z_save;
doabbr(startbuf, zp, NULL, zp->z_isdst, save, false);
- type = addtype(oadd(zp->z_stdoff, save),
+ type = addtype(zp->z_stdoff + save,
startbuf, zp->z_isdst, startttisstd,
startttisut);
if (usestart) {
@@ -3600,7 +3611,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
r->r_filenum, r->r_linenum);
offset = r->r_todisut ? 0 : stdoff;
if (!r->r_todisstd)
- offset = oadd(offset, save);
+ offset += save;
jtime = r->r_temp;
jtime = tadd(jtime, -offset);
if (k < 0 || jtime < ktime) {
@@ -3624,7 +3635,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
rp->r_todo = false;
if (useuntil && ktime >= untiltime) {
if (!*startbuf
- && (oadd(zp->z_stdoff, rp->r_save)
+ && (zp->z_stdoff + rp->r_save
== startoff))
doabbr(startbuf, zp, rp->r_abbrvar,
rp->r_isdst, rp->r_save,
@@ -3636,8 +3647,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
usestart = false;
if (usestart) {
if (ktime < starttime) {
- startoff = oadd(zp->z_stdoff,
- save);
+ startoff = zp->z_stdoff + save;
doabbr(startbuf, zp,
rp->r_abbrvar,
rp->r_isdst,
@@ -3646,8 +3656,8 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
continue;
}
if (*startbuf == '\0'
- && startoff == oadd(zp->z_stdoff,
- save)) {
+ && startoff == (zp->z_stdoff
+ + save)) {
doabbr(startbuf,
zp,
rp->r_abbrvar,
@@ -3660,7 +3670,7 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
rp->r_filenum, rp->r_linenum);
doabbr(ab, zp, rp->r_abbrvar,
rp->r_isdst, rp->r_save, false);
- offset = oadd(zp->z_stdoff, rp->r_save);
+ offset = zp->z_stdoff + rp->r_save;
type = addtype(offset, ab, rp->r_isdst,
rp->r_todisstd, rp->r_todisut);
if (defaulttype < 0 && !rp->r_isdst)
--
2.53.0