Skip to content

Cap EventCommandList's param_count to what the buffer actually holds - #2

Open
Sqweedward wants to merge 2 commits into
RPG-Maker-Translation-Tools:mainfrom
Sqweedward:fix/event-command-param-count-oom
Open

Sqweedward wants to merge 2 commits into
RPG-Maker-Translation-Tools:mainfrom
Sqweedward:fix/event-command-param-count-oom

Conversation

@Sqweedward

Copy link
Copy Markdown

Fixes #1.

EventCommandRef::param_count previously stored the raw, unvalidated header value verbatim. ParamIter implements ExactSizeIterator off that count, so EventCommandList::read's parameters: cmd.params().collect() takes the Vec::with_capacity(size_hint) fast path and reserves for the declared count before reading a single parameter — a corrupted/truncated file declaring e.g. u32::MAX parameters on a file of a few dozen bytes causes an attempted multi-gigabyte allocation and an abort, not a graceful parse error.

The command-parsing loop in EventCommandIter::next already scans parameters one at a time and stops the moment varint() runs out of data — it already knows the actual count. This just tracks that instead of the declared header value, and reports DiagKind::ShortChunk when they differ (the same diagnostic List::read_inline already uses for the equivalent "declared more than is present" case elsewhere in this crate, so the fix follows an existing convention rather than inventing a new one).

Verification

Happy to adjust the diagnostic choice or add more coverage if you'd like something different.

Sqweedward and others added 2 commits September 9, 2026 11:57
EventCommandRef::param_count previously stored the raw, unvalidated
header value verbatim. ParamIter implements ExactSizeIterator off that
count, so EventCommandList::read's parameters: cmd.params().collect()
takes the Vec::with_capacity(size_hint) fast path and reserves for the
declared count before reading a single parameter - a corrupted/
truncated file declaring e.g. u32::MAX parameters on a file of a few
dozen bytes causes an attempted multi-gigabyte allocation and an abort,
not a graceful parse error.

The command-parsing loop already scans parameters one at a time and
stops the moment varint() runs out of data, so it already knows the
*actual* count - track that instead of the declared header value and
report DiagKind::ShortChunk when they differ, the same diagnostic
List::read_inline already uses for the equivalent "declared more than
is present" case elsewhere in this crate.

Fixes RPG-Maker-Translation-Tools#1. Confirmed against the fuzzer-found repro from that issue
(minimized to 19 bytes) - no longer aborts. Also ran the reporter's own
fuzz_database harness for ~150k iterations against this fix with no new
crash.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The existing test only covered the all-or-nothing case (u32::MAX
declared, zero bytes available). Add the more telling case: some
parameters genuinely present, fewer than declared - confirms the fix
keeps exactly what's there (values, count) rather than just zeroing
out on any mismatch, and that the following next() call terminates
cleanly on the now-exhausted buffer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EventCommandList::read can allocate gigabytes from an attacker-controlled param_count (unbounded collect() via ParamIter's ExactSizeIterator)

1 participant