ANT-2026-GBSMA7AY · tz
stack-buffer-overflow high
Severity Claude high · Security research firm high · Maintainer -
Discovered by an unreleased Anthropic model
Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Ada Logics.
ANT-2026-GBSMA7AY: Stack buffer overflow via abbreviation suffix-alias expansion
In zic's writezone(), abbreviations are copied into a 256-byte stack array thischars[TZ_MAX_CHARS] via an unchecked strcpy at zic.c:2958. The implicit size invariant is broken because addtype() searches chars[] byte-by-byte, letting many distinct types share suffix-aliased storage inside one long abbreviation string. If the type holding the full string is omitted in pass 1 (via limitrange() dropping pre-1901 transitions) while increasing-length suffix types survive, each suffix fails the dedup scan and is copied independently, summing to far more than 256 bytes. An attacker who supplies a crafted .zi source controls the abbreviation bytes and can overflow past thischars into omittype, typemap, indmap and ultimately writezone()'s saved return address (~32KB overflow achievable within TZ_MAX_TYPES).
Target
Project: tz
Version: commit 37a4d178474e3b85faede06e44abda4d0b2a4da0 (still present at HEAD)
Location: zic.c:2958
Technical Details
Generated a 63-line .zi file: Rule at year 1800 with a 200-char LETTERS field and SAVE=1:00 (DST), a non-DST decoy Rule at 1850, and 60 post-1901 Rules whose LETTERS are increasing-length suffixes of the long string with distinct SAVE values. ASan-built zic crashed with stack-buffer-overflow ... WRITE of size 23 ... [2112,2368) 'thischars' (line 2803) <== Memory access at offset 2368 overflows this variable. The strcpy at zic.c:2958 writes past the 256-byte stack array exactly as described. CORRECTION to the finding's recipe: the long-string rule must be DST (SAVE != 0) — otherwise outzone() picks it as the global defaulttype (zic.c:3668), range64.defaulttype carries it into pass 1 via the override at zic.c:2858, and it is never omitted. With the long type DST and the decoy non-DST, the decoy becomes both range32 and range64 defaulttype, type 0 is omitted in pass 1, and the suffix copies overflow.
Crash trace (truncated — full trace in attached crash.log):
warning: "/tmp/overflow.zi", line 63: time zone abbreviation has too many characters (ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr) (rule from "/tmp/overflow.zi", line 1)
=================================================================
==18==ERROR: AddressSanitizer: stack-buffer-overflow on address 0xffff95501940 at pc 0xaaaad313a6e0 bp 0xffffcff54230 sp 0xffffcff53a20
WRITE of size 23 at 0xffff95501940 thread T0
==18==WARNING: invalid path to external symbolizer!
==18==WARNING: Failed to use and restart external symbolizer!
#0 0xaaaad313a6dc (/work/zic+0xca6dc) (BuildId: d777d845fbc562b7a70e47afca8be13d4322602c)
#1 0xaaaad31a3c40 (/work/zic+0x133c40) (BuildId: d777d845fbc562b7a70e47afca8be13d4322602c)
#2 0xaaaad319721c (/work/zic+0x12721c) (BuildId: d777d845fbc562b7a70e47afca8be13d4322602c)
#3 0xffff96ee2258 (/lib/aarch64-linux-gnu/libc.so.6+0x22258) (BuildId: 45918bc10b33fd96afc550c98de062dccdf44328)
#4 0xffff96ee2338 (/lib/aarch64-linux-gnu/libc.so.6+0x22338) (BuildId: 45918bc10b33fd96afc550c98de062dccdf44328)
#5 0xaaaad30b116c (/work/zic+0x4116c) (BuildId: d777d845fbc562b7a70e47afca8be13d4322602c)
Address 0xffff95501940 is located in stack of thread T0 at offset 2368 in frame
#0 0xaaaad31a23f8 (/work/zic+0x1323f8) (BuildId: d777d845fbc562b7a70e47afca8be13d4322602c)
This frame has 23 object(s):
[32, 36) 'buf.i2444' (line 2576)
[48, 52) 'buf.i.i2409' (line 2576)
[64, 72) 'buf.i2410' (line 2588)
[96, 100) 'buf.i2394' (line 2576)
[112, 116) 'buf.i.i2359' (line 2576)
[128, 136) 'buf.i2360' (line 2588)
[160, 164) 'buf.i2342' (line 2576)
[176, 180) 'buf.i.i2307' (line 2576)
[192, 200) 'buf.i2308' (line 2588)
[224, 228) 'buf.i.i2272' (line 2576)
[240, 248) 'buf.i2273' (line 2588)
[272, 276) 'buf.i.i' (line 2576)
[288, 296) 'buf.i2257' (line 2588)
[320, 324) 'buf.i' (line 2576)
[336, 344) 'tempname' (line 2659)
[368, 376) 'outname' (line 2660)
[400, 448) 'byval-temp' (line 2758)
[480, 528) 'byval-temp380' (line 2762)
[560, 604) 'tzh' (line 2795)
[640, 896) 'omittype' (line 2800)
[960, 1984) 'typemap' (line 2801)
[2112, 2368) 'thischars' (line 2803) <== Memory access at offset 2368 overflows this variable
[2432, 3456) 'indmap' (line 2806)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/work/zic+0xca6dc) (BuildId: d777d845fbc562b7a70e47afca8be13d4322602c)
Reproduction
- Create a .zi file with
Zone Foo 0:00 R %s. - Add
Rule R 1800 only - Jan 1 0:00 1:00 <254-char LETTERS>— the full-string DST type occupying chars[0..254]. - Add a non-DST decoy
Rule R 1850 only - Jan 1 0:00 0 Qso limitrange() in pass 1 makes the decoy the defaulttype and omits type 0. - Add 22+ Rules at years 1910,1911,... with distinct SAVE values and LETTERS fields that are increasing-length suffixes of the long string; addtype()'s byte-wise search aliases each to a distinct desigidx inside the existing string.
- Run
zic -d /tmp/zout overflow.zi. - In writezone() pass 1, type 0 is omitted; each suffix type has a unique desigidx (indmap miss) and fails the strcmp dedup against shorter prior entries, so strcpy at zic.c:2958 appends each one.
- thischarcnt exceeds 256 (e.g. 275 bytes for 22 suffixes, up to ~32KB for ~254 suffixes), overflowing thischars into adjacent stack objects and the saved return address.
[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]
Acknowledgement
This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-GBSMA7AY.
Reference: ANT-2026-GBSMA7AY
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure
Triage and disclosure were performed by Ada Logics.
- Verdict
- true positive
- Severity
- high
Dates from discovery through public reveal.
- 2026-04-08 Reported to tracker
- 2026-04-15 Sent to maintainer
- 2026-04-23 Patch released
- 2026-05-07 Maintainer acknowledged
- 2026-07-08 Publicly revealed
SHA-3-512 hash:
df284ea74e40921d64c0eed1c08fedd31735b920612dfa66e0f26a76259eeea32706f84635c3390475dd2010a82257cd100feb7e9914ef3c1b0718bab693bf12
Committed 2026-05-07 07:53 PT
Revealed 2026-07-08 15:59 PT
Verify (download preimage.json)
Show preimage JSON
{
"ant_id": "ANT-2026-GBSMA7AY",
"bug_class": "Stack-buffer-overflow",
"claude_severity": "high",
"commit_sha": null,
"created_at": "2026-04-09T05:38:42+00:00",
"description": "In zic's writezone(), abbreviations are copied into a 256-byte stack array `thischars[TZ_MAX_CHARS]` via an unchecked strcpy at zic.c:2958. The implicit size invariant is broken because addtype() searches chars[] byte-by-byte, letting many distinct types share suffix-aliased storage inside one long abbreviation string. If the type holding the full string is omitted in pass 1 (via limitrange() dropping pre-1901 transitions) while increasing-length suffix types survive, each suffix fails the dedup scan and is copied independently, summing to far more than 256 bytes. An attacker who supplies a crafted .zi source controls the abbreviation bytes and can overflow past thischars into omittype, typemap, indmap and ultimately writezone()'s saved return address (~32KB overflow achievable within TZ_MAX_TYPES).",
"discovered_at": null,
"location": "zic.c:2958",
"poc_sha256": null,
"preimage_version": 1,
"project": "tz",
"reproduction": [
"1. Create a .zi file with `Zone Foo 0:00 R %s`.",
"2. Add `Rule R 1800 only - Jan 1 0:00 1:00 <254-char LETTERS>` — the full-string DST type occupying chars[0..254].",
"3. Add a non-DST decoy `Rule R 1850 only - Jan 1 0:00 0 Q` so limitrange() in pass 1 makes the decoy the defaulttype and omits type 0.",
"4. Add 22+ Rules at years 1910,1911,... with distinct SAVE values and LETTERS fields that are increasing-length suffixes of the long string; addtype()'s byte-wise search aliases each to a distinct desigidx inside the existing string.",
"5. Run `zic -d /tmp/zout overflow.zi`.",
"6. In writezone() pass 1, type 0 is omitted; each suffix type has a unique desigidx (indmap miss) and fails the strcmp dedup against shorter prior entries, so strcpy at zic.c:2958 appends each one.",
"7. thischarcnt exceeds 256 (e.g. 275 bytes for 22 suffixes, up to ~32KB for ~254 suffixes), overflowing thischars into adjacent stack objects and the saved return address."
],
"technical_details": "Generated a 63-line .zi file: Rule at year 1800 with a 200-char LETTERS field and SAVE=1:00 (DST), a non-DST decoy Rule at 1850, and 60 post-1901 Rules whose LETTERS are increasing-length suffixes of the long string with distinct SAVE values. ASan-built zic crashed with `stack-buffer-overflow ... WRITE of size 23 ... [2112,2368) 'thischars' (line 2803) <== Memory access at offset 2368 overflows this variable`. The strcpy at zic.c:2958 writes past the 256-byte stack array exactly as described. CORRECTION to the finding's recipe: the long-string rule must be DST (SAVE != 0) — otherwise outzone() picks it as the global defaulttype (zic.c:3668), range64.defaulttype carries it into pass 1 via the override at zic.c:2858, and it is never omitted. With the long type DST and the decoy non-DST, the decoy becomes both range32 and range64 defaulttype, type 0 is omitted in pass 1, and the suffix copies overflow.",
"title": "Stack buffer overflow via abbreviation suffix-alias expansion",
"vendor_severity": "high"
}