Skip to content

ods: fix DoS via repeated rows/columns (issue #594)#596

Merged
jmcnamara merged 3 commits intotafia:masterfrom
withzombies:fix-ods-dos-issue-594
Jan 13, 2026
Merged

ods: fix DoS via repeated rows/columns (issue #594)#596
jmcnamara merged 3 commits intotafia:masterfrom
withzombies:fix-ods-dos-issue-594

Conversation

@withzombies
Copy link
Contributor

Fix DoS vulnerability where malicious ODS files can exhaust memory by declaring billions of repeated cells via table:number-rows-repeated and table:number-columns-repeated XML attributes.

  • Add caps matching XLSX limits: 1M rows, 16K columns, 100M total cells
  • Return OdsError::CellLimitExceeded instead of silently returning empty range when limits exceeded
  • A 7KB malicious file can no longer attempt to allocate memory for 17+ billion cells

Fixes #594

Add limits to prevent memory exhaustion from malicious ODS files that
declare billions of repeated cells via table:number-rows-repeated and
table:number-columns-repeated attributes.

Protection layers:
- Cap columns per row at MAX_COLUMNS (16,384)
- Cap total row repeats at MAX_ROWS (1,048,576)
- Cap total cells at MAX_CELLS (100 million) in get_range()

These limits match XLSX's existing row/column limits and prevent a 7KB
malicious file from attempting to allocate memory for 17+ billion cells.

When MAX_CELLS is exceeded, return OdsError::CellLimitExceeded instead
of silently returning an empty range. This ensures callers are properly
informed of truncation rather than receiving silent data loss.
Copy link
Collaborator

@jmcnamara jmcnamara left a comment

Choose a reason for hiding this comment

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

Thanks, good work.

All reviews are comments except for the test which should be changed.

src/ods.rs Outdated
match reader.read_event_into(&mut buf) {
Ok(Event::Start(e)) if e.name() == QName(b"table:table-row") => {
let row_repeats = match e.try_get_attribute(b"table:number-rows-repeated")? {
let row_repeats: usize = match e.try_get_attribute(b"table:number-rows-repeated")? {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Explicit usize isn't required here. This is minor, and harmless, but the reason I mention it is that an explicit type makes me stop to parse why it is required.

This comment applies to the other places usize has been added without being required. You can decide if they should be implicit types instead.

src/ods.rs Outdated
Comment on lines +513 to +514
let mut empty_row_repeats = 0usize;
let mut consecutive_empty_rows = 0usize;
Copy link
Collaborator

Choose a reason for hiding this comment

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

I prefer empty_row_repeats = 0_usize for legibility or empty_row_repeats: usize = 0 which you use elsewhere.

The type isn't required for consecutive_empty_rows.

- Use static test file
- Remove extra type annotations
@withzombies withzombies requested a review from jmcnamara January 13, 2026 17:45
@jmcnamara jmcnamara merged commit a1b5768 into tafia:master Jan 13, 2026
5 checks passed
@jmcnamara
Copy link
Collaborator

@withzombies Merged. Thanks.

@withzombies withzombies deleted the fix-ods-dos-issue-594 branch January 23, 2026 13:47
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.

Bug: DoS via repeated rows/columns in ODS files

2 participants