fix(key): Document text lifetime and guard matchText against torn slices#325
Merged
Merged
Conversation
rockorager
requested changes
Apr 29, 2026
Owner
rockorager
left a comment
There was a problem hiding this comment.
I'm ok with the documentation change, but not the change to limit the text field to max 4 bytes, that is incorrect for how the key is used.
0c7edf3 to
f5a9537
Compare
Contributor
Author
|
You're right — paste and IME break the 4-byte assumption. Dropped the cap, kept the doc-only path. The downstream that hit this fixes its side by reading |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Key.text, when populated, is a slice into the parser's per-event scratch buffer. The buffer is reused on the next decode, so aKeythat outlives one parser turn — for example, an event queued to a worker thread or held across aLoopturn — ends up pointing at the next event's bytes.matchTextthen compares against arbitrary memory and either returns wrong matches or dereferences a torn slice header into freed memory.Change
Two pieces, both in
src/Key.zig:Doc the lifetime contract. The doc comment on
matchText(and by implicationKey.text) now states explicitly thattextis parser-scoped and callers retaining aKeymust copy. This is the actual fix — the bug is a usage contract that wasn't written down.Defensive bounds inside
matchText. A single keystroke produces at most 4 UTF-8 bytes. Anything longer is unambiguously a corrupted slice header, so we bail rather than read. Also pulls the optional unwrap out of the chain so the body isn't sprinkled withself.text.?.No API change, no platform impact, no new dependencies.
Test plan
zig buildclean against currentmain(post-Zig 0.16 port #316).zig build testgreen.Notes
Found while debugging a downstream consumer that drains events on a different thread than the input parser runs on. The lifetime invariant is real and worth documenting regardless of whether the bounds guard lands; happy to drop the guard if you'd rather force callers to honour the doc-comment.
Closes — none directly. Adjacent to the general "events crossing thread boundaries" surface.