Skip to content

Avoid appending after torn item headers - #137

Closed
enomado wants to merge 1 commit into
tweedegolf:masterfrom
enomado:fix/power-cut-phantom-header
Closed

enomado wants to merge 1 commit into
tweedegolf:masterfrom
enomado:fix/power-cut-phantom-header

Conversation

@enomado

@enomado enomado commented Aug 30, 2026

Copy link
Copy Markdown

Closes #136.

Problem

After an item-header write is interrupted, ItemHeaderIter skips the corrupt bytes one word at a time. The append scan then used the first erased byte as the next item address. A new header written at that address could complete a CRC-valid phantom header starting inside the torn header. That phantom item overlaps the real new item, so a clean store_item could return Ok(()) while a fresh storage instance fetched None.

Fix

Track whether ItemHeaderIter encountered corruption during the append scan. If it did, find_next_free_item_spot reports that the page has no safe append location. Existing map/queue rotation then closes that page and writes on a clean page instead of placing a header directly after torn bytes.

Verification

  • Added a deterministic regression using MockFlashBase: two shutdowns after seven bytes, reconstruction after each, then a clean store and another reconstruction.
  • Added map-power-cuts, a state-machine fuzz target that repeatedly injects byte-exact shutdowns, destroys all cache state, reconstructs MapStorage, and checks the old-or-new durability invariant.
  • cargo test --features arrayvec,heapless,heapless-09,alloc,postcard,shared-ram-ring: 61 passed; doctests passed.
  • cargo clippy --features arrayvec,heapless,heapless-09,alloc,postcard,shared-ram-ring -- -D warnings: passed.
  • cargo fuzz run --sanitizer none map-power-cuts -- -max_total_time=30: 212,258 executions, no failures.

@diondokter diondokter left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for reporting!
I'm just not sure this is the best fix we can think of. Left some thoughts in the comments. What do you think?

Comment thread src/item.rs
}
Err(Error::Corrupted { .. }) => {
self.encountered_corruption = true;
self.current_address += S::WORD_SIZE as u32;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if it would also be fixed by jumping ahead a full header instead of jumping by a word...

Comment thread src/item.rs
Comment on lines +514 to +521
// A torn header can end immediately before this erased area.
// Writing a new header there could complete a valid-looking
// phantom header that overlaps the new item. Stop using the
// page instead; callers will rotate to a clean page.
if headers.encountered_corruption {
return Ok(None);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Jumping to a new page seems a bit wasteful. Say someone has a noisy SPI or a bad NOR chip and so there's a 0.01% of a bit flip for each bit. Then on a 4k page there'll be 3 wrong bits and so up to 3 corrupted items. We'd waste a lot of space by skipping 2/3rd of pages on average.

That percentage is unrealistically high I think, but still

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure about this new fuzz target.
The real solution is to have the existing fuzz targets be able to insert multiple shutoffs (instead of the fixed 1 shutoff they now have)

@diondokter

Copy link
Copy Markdown
Member

Also, did you use AI to generate this PR?
I don't take AI contributions on this project: https://github.com/tweedegolf/sequential-storage/blob/master/CONTRIBUTING.md

@enomado

enomado commented Aug 31, 2026

Copy link
Copy Markdown
Author

I dont do non-AI contributions as well.
Felt it was my duty to inform you that there is a problem.

https://github.com/enomado/enomado/blob/main/AI_POLICY.md

@diondokter diondokter closed this Aug 31, 2026
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.

store_item can acknowledge an item that disappears after reboot

2 participants