81 lines
3.0 KiB
Diff
81 lines
3.0 KiB
Diff
From 3b7b4155570b4b9054465785be2992c92cb7d7b1 Mon Sep 17 00:00:00 2001
|
|
From: Rich Felker <dalias@aerifal.cx>
|
|
Date: Wed, 9 Feb 2022 17:48:43 -0500
|
|
Subject: fix out-of-bound read processing time zone data with distant-past
|
|
dates
|
|
|
|
this bug goes back to commit 1cc81f5cb0df2b66a795ff0c26d7bbc4d16e13c6
|
|
where zoneinfo file support was first added. in scan_trans, which
|
|
searches for the appropriate local time/dst rule in effect at a given
|
|
time, times prior to the second transition time caused the -1 slot of
|
|
the index to be read to determine the previous rule in effect. this
|
|
memory was always valid (part of another zoneinfo table in the mapped
|
|
file) but the byte value read was then used to index another table,
|
|
possibly going outside the bounds of the mmap. most of the time, the
|
|
result was limited to misinterpretation of the rule in effect at that
|
|
time (pre-1900s), but it could produce a crash if adjacent memory was
|
|
not readable.
|
|
|
|
the root cause of the problem, however, was that the logic for this
|
|
code path was all wrong. as documented in the comment, times before
|
|
the first transition should be treated as using the lowest-numbered
|
|
non-dst rule, or rule 0 if no non-dst rules exist. if the argument is
|
|
in units of local time, however, the rule prior to the first
|
|
transition is needed to determine if it falls before or after it, and
|
|
that's where the -1 index was wrongly used.
|
|
|
|
instead, use the documented logic to find out what rule would be in
|
|
effect before the first transition, and apply it as the offset if the
|
|
argument was given in local time.
|
|
|
|
the new code has not been heavily tested, but no longer performs
|
|
potentially out-of-bounds accesses, and successfully handles the 1883
|
|
transition from local mean time to central standard time in the test
|
|
case the error was reported for.
|
|
---
|
|
src/time/__tz.c | 26 ++++++++++++--------------
|
|
1 file changed, 12 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/src/time/__tz.c b/src/time/__tz.c
|
|
index 3e2fcdcb..c34b3eb7 100644
|
|
--- a/src/time/__tz.c
|
|
+++ b/src/time/__tz.c
|
|
@@ -293,22 +293,20 @@ static size_t scan_trans(long long t, int local, size_t *alt)
|
|
n = (index-trans)>>scale;
|
|
if (a == n-1) return -1;
|
|
if (a == 0) {
|
|
- x = zi_read32(trans + (a<<scale));
|
|
- if (scale == 3) x = x<<32 | zi_read32(trans + (a<<scale) + 4);
|
|
+ x = zi_read32(trans);
|
|
+ if (scale == 3) x = x<<32 | zi_read32(trans + 4);
|
|
else x = (int32_t)x;
|
|
- if (local) off = (int32_t)zi_read32(types + 6 * index[a-1]);
|
|
+ /* Find the lowest non-DST type, or 0 if none. */
|
|
+ size_t j = 0;
|
|
+ for (size_t i=abbrevs-types; i; i-=6) {
|
|
+ if (!types[i-6+4]) j = i-6;
|
|
+ }
|
|
+ if (local) off = (int32_t)zi_read32(types + j);
|
|
+ /* If t is before first transition, use the above-found type
|
|
+ * and the index-zero (after transition) type as the alt. */
|
|
if (t - off < (int64_t)x) {
|
|
- for (a=0; a<(abbrevs-types)/6; a++) {
|
|
- if (types[6*a+4] != types[4]) break;
|
|
- }
|
|
- if (a == (abbrevs-types)/6) a = 0;
|
|
- if (types[6*a+4]) {
|
|
- *alt = a;
|
|
- return 0;
|
|
- } else {
|
|
- *alt = 0;
|
|
- return a;
|
|
- }
|
|
+ if (alt) *alt = index[0];
|
|
+ return j/6;
|
|
}
|
|
}
|
|
|
|
--
|
|
cgit v1.2.1
|
|
|