fix(xlsx): checked arithmetic in get_row_and_optional_column - #705
Closed
Svector-anu wants to merge 1 commit into
Closed
fix(xlsx): checked arithmetic in get_row_and_optional_column#705Svector-anu wants to merge 1 commit into
Svector-anu wants to merge 1 commit into
Conversation
get_row_and_optional_column backs get_dimension/get_row_column/get_row -
the worksheet dimension and merged region parsing paths. it accumulates
the column (base-26) and row (base-10) values with raw */+. a range
string with a long enough run of letters or digits overflows u32 during
accumulation and panics ("attempt to multiply with overflow") instead of
returning an error.
Reference::parse already guards the same input class, via wrapping
arithmetic plus a post-hoc validate() bounds check. this function's
callers don't do a follow-up bounds check, so checked arithmetic that
fails right at the overflow site is the more self-contained fix here -
reusing the existing ColumnNumberOverflow/RowNumberOverflow errors
already defined for the same condition elsewhere in the file.
found by fuzzing a downstream consumer's xlsx target (seeded from its
own test fixtures) with cargo-fuzz: a crafted xlsx whose <dimension> or
a merged-cell reference contains a ~20-character run of letters aborts
the whole parse. no memory-safety impact, but any service converting
untrusted spreadsheets through calamine can be crashed by a small file.
reproducer and regression test included.
Svector-anu
force-pushed
the
fix/xlsx-range-parse-overflow-panic
branch
from
August 8, 2026 11:57
dc88a76 to
69a27bd
Compare
Collaborator
|
Is this similar to #696? |
Author
|
yeah, same root cause - unchecked u32 accumulation in get_row_and_optional_column. #696 predates this by a couple weeks and is further along (already reworked per your feedback to validate against the actual row/column limits instead of just catching the overflow). closing this one in favor of that. |
Svector-anu
added a commit
to Svector-anu/svectors-lab
that referenced
this pull request
Aug 10, 2026
Run the target's own cargo-fuzz harness (when it ships one) as part of arm A: seed the corpus from tests/fixtures where they exist, run each target for ~90s (capped at 8 targets), and route a crash through the same triage as any scanner hit before it counts as a finding. Crash routing depends on where it lands: target's own code -> PVR, a dependency -> public PR against that dependency, harness lying -> drop. Verified end to end against firecrawl/anydoc: its xlsx fuzz target crashed on an integer-overflow panic inside calamine (a dependency), root-caused, fixed, and filed upstream (tafia/calamine#705). Secrets are scrubbed from the fuzz env (env -u GH_TOKEN -u GH_GLOBAL -u RESEND_*) immediately before compiling/running the target's own code, since a malicious target's build.rs could otherwise exfiltrate them. Split out of aeonfun#863 per triage: this PR is skill-scope only (skills/vuln-scanner/SKILL.md + eyebrowlock.json). The paired runtime-infrastructure changes (.github/workflows/aeon.yml, scripts/stage-vuln-scanner.sh, scripts/skill_mode.sh) are in a separate follow-up PR for a maintainer to review, since they touch the fork's trust boundary (Bash(cargo:*) grant, toolchain staging).
aaronjmars
pushed a commit
to aeonfun/aeon
that referenced
this pull request
Aug 10, 2026
) Run the target's own cargo-fuzz harness (when it ships one) as part of arm A: seed the corpus from tests/fixtures where they exist, run each target for ~90s (capped at 8 targets), and route a crash through the same triage as any scanner hit before it counts as a finding. Crash routing depends on where it lands: target's own code -> PVR, a dependency -> public PR against that dependency, harness lying -> drop. Verified end to end against firecrawl/anydoc: its xlsx fuzz target crashed on an integer-overflow panic inside calamine (a dependency), root-caused, fixed, and filed upstream (tafia/calamine#705). Secrets are scrubbed from the fuzz env (env -u GH_TOKEN -u GH_GLOBAL -u RESEND_*) immediately before compiling/running the target's own code, since a malicious target's build.rs could otherwise exfiltrate them. Split out of #863 per triage: this PR is skill-scope only (skills/vuln-scanner/SKILL.md + eyebrowlock.json). The paired runtime-infrastructure changes (.github/workflows/aeon.yml, scripts/stage-vuln-scanner.sh, scripts/skill_mode.sh) are in a separate follow-up PR for a maintainer to review, since they touch the fork's trust boundary (Bash(cargo:*) grant, toolchain staging).
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
get_row_and_optional_columnbacksget_dimension/get_row_column/get_row- the worksheet dimension and merged region parsing paths. it accumulates the base-26 column value and base-10 row value with raw*/+. a range string with a long enough run of letters or digits overflowsu32during accumulation and panics (attempt to multiply with overflow) instead of returning an error.Reference::parsealready guards the same input class viawrapping_mul/wrapping_addplus a post-hocvalidate()bounds check (seetest_parse_reference_overflow).get_row_and_optional_column's callers don't do a follow-up bounds check, so checked arithmetic that fails right at the overflow site is the more self-contained fix here - reusing theColumnNumberOverflow/RowNumberOverflowerrors that already exist for the same condition elsewhere in the file.how this was found
fuzzing a downstream consumer's
xlsxtarget (seeded from its own test fixtures) crashed within a couple minutes:call stack:
get_dimension->get_row_and_optional_column, reached while parsing a crafted xlsx'sxl/worksheets/sheet1.xml.no memory-safety impact - rust panics safely - but any service converting untrusted spreadsheets through calamine can be crashed by a small file, since a release build without overflow checks would instead silently wrap into a bogus column/row value with no validation downstream of this function.
what's included
ColumnNumberOverflow/RowNumberOverflowvariantstest_get_row_and_optional_column_overflow) reproducing the panic pre-fix, plus aget_dimensioncase through the actual crash pathcargo test --lib(43 passed) andcargo clippy --lib -- -D warningsboth cleanopen to matching
Reference::parse's wrapping+validate pattern instead if you'd rather - went with checked arithmetic mainly because this function's callers don't validate the result afterward.