Skip to content

xls: fix panic on a PtgExp token with a truncated operand - #707

Merged
jmcnamara merged 1 commit into
tafia:masterfrom
Butch78:fix/ptgexp-truncated-operand
Aug 15, 2026
Merged

xls: fix panic on a PtgExp token with a truncated operand#707
jmcnamara merged 1 commit into
tafia:masterfrom
Butch78:fix/ptgexp-truncated-operand

Conversation

@Butch78

@Butch78 Butch78 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Current behavior

parse_formula advances past a PtgExp token by slicing four bytes off rgce without checking that four bytes remain:

// src/xls.rs:1515
0x01 => {
    // PtgExp: array/shared formula, ignore
    debug!("ignoring PtgExp array/shared formula");
    stack.push(formula.len());
    rgce = &rgce[4..];        // ← no length check
}

A workbook whose PtgExp operand is truncated panics:

thread 'main' panicked at src/xls.rs:1515:29:
range start index 4 out of range for slice of length 2

Because formulas are parsed while the workbook is read, this aborts open_workbook itself — the file cannot be opened at all, let alone have its formulas inspected. Any caller handing calamine a file it did not produce (an upload, a corpus, a user attachment) is one malformed workbook away from losing the thread.

Expected behavior

A truncated token is malformed input, not a bug in the caller, so it should surface as an Err like the reader's other length violations — and the rest of the workbook should still load.

Sample code to reproduce

use calamine::{open_workbook, Reader, Xls};

fn main() {
    // panics before this returns
    let mut wb: Xls<_> = open_workbook("ptgexp-truncated-operand.xls").unwrap();
    println!("{:?}", wb.sheet_names());
}

The fix

Report the short token as XlsError::Len, matching how the reader already handles other length violations (e.g. the Formula record check at src/xls.rs:640):

if rgce.len() < 4 {
    return Err(XlsError::Len {
        expected: 4,
        found: rgce.len(),
        typ: "PtgExp",
    });
}
rgce = &rgce[4..];

With the fix the attached workbook opens and yields real data — a 23×10 range and 22 formula cells — so this recovers the file rather than merely avoiding the crash. That is what the test asserts.

Test file

tests/ptgexp-truncated-operand.xls, from the public Enron/EDRM corpus via SheetJS. It is the only file in a 2000-workbook sample that triggers this, which is presumably why it has not been reported before.

Test added to tests/test.rs in the existing style; it panics without the fix and passes with it.

Environment

- calamine version: 0.36.1 (also reproduced on master @ 0a24c2a)
- Cargo.toml dependency line for calamine: calamine = "0.36"
- rustc version: nightly
- OS: Linux

Checks

cargo fmt -- --check, cargo clippy --all-targets --all-features -- -Dwarnings, cargo test --all-features, and cargo check --target wasm32-unknown-unknown --all-features all pass locally.

A note on the wider pattern

Every arm of parse_formula advances rgce with an unchecked slice, so this class of panic is not exhausted — #270, #447, #600, #612 and #643 were each the same shape in a different token. I have kept this PR to the one token I can demonstrate with a fixture rather than sweeping the function, since a broad change to a parser I do not maintain is harder to review. Happy to follow up with a systematic guard if you would find that useful.

Copilot AI lite review requested due to automatic review settings August 10, 2026 11:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment thread src/xls.rs Outdated
Comment thread tests/test.rs Outdated
@jmcnamara

Copy link
Copy Markdown
Collaborator

Thanks for the fix.

I have made some comments inline. Please address them and rebase and force push the changes to your branch. Then mark the comments as "resolved".

Also, reduce the commit message down to 1-2 lines max.

@jmcnamara jmcnamara self-assigned this Aug 14, 2026
@jmcnamara jmcnamara added next_release needs work for merge The PR needs some rework or clarification. No suitable for merge, yet. labels Aug 14, 2026
Report the short token as XlsError::Len instead of slicing past the end of rgce.
@Butch78
Butch78 force-pushed the fix/ptgexp-truncated-operand branch from 39f66c5 to 976372a Compare August 15, 2026 12:09
@Butch78

Butch78 commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

Added all your suggestions, thanks for everything you do with Calamine! :)

@jmcnamara
jmcnamara merged commit 263762c into tafia:master Aug 15, 2026
6 checks passed
@jmcnamara

Copy link
Copy Markdown
Collaborator

Merged. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs work for merge The PR needs some rework or clarification. No suitable for merge, yet. next_release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants