You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
StreamHeader.read (src/rootfilespec/bootstrap/streamedobject.py:53-61) recognises only "byte count + version", "byte count + class record" and "bare word = object reference". Three shapes the spec describes fall outside that:
Object slots without a byte count (:54-58). Buffer framing §6 lists five slot forms, two with no byte count: a bare kNewClassTag and a bare kClassMask | p, each followed by the object. A word like 0x80000055 has kByteCountMask clear, so it is taken for an object reference and the object body is read as the next member — a silent desync. A bare 0xFFFFFFFF is worse: read as ">i" it is -1, the mask test is truthy, and the "byte count" becomes -1073741825. Not seen in the corpora (ROOT does not write these today); derived from the code, not reproduced on a file.
Negative version words (:27-28, :61). kNotAVersion = 0x8000 is tested on the first u16 after the byte count, but §4 says "The trigger is version <= 0, so a negative version word takes the same path." A version word such as 0xFFFF would be routed into the class-record branch. Hardening; no known file.
Suggested fix: make the slot parser enumerate §6's forms explicitly (including "byte count then a tag that is an object reference"), read the first word unsigned, and fail loudly on anything unrecognised rather than returning a Ref.
Severity / size: medium (1–2 are silent desyncs; 2 blocks #18's file) / S–M.
Related:#18, #105, #104, #74 (a slot with no byte count cannot be skipped and must still fail loudly).
StreamHeader.read(src/rootfilespec/bootstrap/streamedobject.py:53-61) recognises only "byte count + version", "byte count + class record" and "bare word = object reference". Three shapes the spec describes fall outside that::54-58). Buffer framing §6 lists five slot forms, two with no byte count: a barekNewClassTagand a barekClassMask | p, each followed by the object. A word like0x80000055haskByteCountMaskclear, so it is taken for an object reference and the object body is read as the next member — a silent desync. A bare0xFFFFFFFFis worse: read as">i"it is-1, the mask test is truthy, and the "byte count" becomes-1073741825. Not seen in the corpora (ROOT does not write these today); derived from the code, not reproduced on a file.uproot-issue413.root'sfLeavesentries are exactly this (40 00 00 04 | 00 00 0a 7f, confirmed in the bytes for TLeafI not declared in StreamerInfo, e.g. uproot-issue413.root #18). The code takes40 00 00 04as a byte count and then reads the tag's high half as a version word. Latent today only because that file fails earlier (TLeafI not declared in StreamerInfo, e.g. uproot-issue413.root #18).:27-28,:61).kNotAVersion = 0x8000is tested on the firstu16after the byte count, but §4 says "The trigger isversion <= 0, so a negative version word takes the same path." A version word such as0xFFFFwould be routed into the class-record branch. Hardening; no known file.Suggested fix: make the slot parser enumerate §6's forms explicitly (including "byte count then a tag that is an object reference"), read the first word unsigned, and fail loudly on anything unrecognised rather than returning a
Ref.Severity / size: medium (1–2 are silent desyncs; 2 blocks #18's file) / S–M.
Related: #18, #105, #104, #74 (a slot with no byte count cannot be skipped and must still fail loudly).
Assisted-by: claude-code:claude-fable-5-1