ANT-2026-GBSMA7AY · tz

stack-buffer-overflow high

Severity Claude high · Security research firm high · Maintainer -

Discovered by an unreleased Anthropic model

REPORT

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

  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.

[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

SECURITY RESEARCH FIRM ANALYSIS

Triage and disclosure were performed by Ada Logics.

Verdict
true positive
Severity
high
TIMELINE

Dates from discovery through public reveal.

  1. 2026-04-08 Reported to tracker
  2. 2026-04-15 Sent to maintainer
  3. 2026-04-23 Patch released
  4. 2026-05-07 Maintainer acknowledged
  5. 2026-07-08 Publicly revealed
PROVENANCE

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"
}