[runtime] append page-size shrink crash safety fix#3837
Conversation
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
commonware-mcp | ca7b55f | May 21 2026, 07:43 AM |
Deploying monorepo with
|
| Latest commit: |
ca7b55f
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://01eb29ad.monorepo-eu0.pages.dev |
| Branch Preview URL: | https://codex-append-resize-crc-fall.monorepo-eu0.pages.dev |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit e9edb5b. Configure here.
There was a problem hiding this comment.
Pull request overview
Fixes crash-safety for runtime’s paged append buffer when performing a same-page shrink of the final partial page by ensuring a shorter CRC can become authoritative without risking loss of the repaired prefix after an interrupted rewrite.
Changes:
- Add a two-phase same-page shrink path in
Append::resize()that stages the new shorter CRC in the alternate slot and then invalidates the old longer CRC. - Update module/docs to reflect same-page shrink semantics and recoverability guarantees.
- Add regression tests for reopening at the shorter size and for surviving an injected interrupted CRC rewrite.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| runtime/src/utils/buffer/paged/mod.rs | Updates paged-buffer documentation to describe same-page shrink CRC behavior and recoverability semantics. |
| runtime/src/utils/buffer/paged/append.rs | Implements same-page shrink CRC rewrite logic and adds regression tests (including a fault-injecting blob wrapper). |
e9edb5b to
31d3ef7
Compare
b0889d3 to
358c7c3
Compare
358c7c3 to
fd03283
Compare
789729a to
6e156b7
Compare
82a86ec to
8768b5d
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
runtime/src/utils/buffer/paged/append.rs:985
new_physical_sizeis computed via(full_pages + 1) * physical_page_size/full_pages * physical_page_sizewithout overflow checks. For very largetarget_size, this can wrapu64and cause truncation/resize to an unintended (and much smaller) physical size. Please compute this withchecked_add/checked_muland returnError::OffsetOverflowon overflow.
let new_physical_size = if partial_bytes > 0 {
// We need full_pages + 1 physical pages to hold the partial data.
// The partial page will be padded to full physical page size.
(full_pages + 1) * physical_page_size
} else {
c465da5 to
8768b5d
Compare
eec4d34 to
16a676d
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
runtime/src/utils/buffer/paged/append.rs:988
new_physical_sizeis computed with unchecked+/*onu64((full_pages + 1) * physical_page_size/full_pages * physical_page_size). Ifresize()is called with a very largetarget_size, these operations can overflow and wrap, causing the underlying blob to be resized to an unintended length. Please usechecked_add/checked_muland returnError::OffsetOverflowon overflow (similar to other offset calculations in this module).
let new_physical_size = if partial_bytes > 0 {
// We need full_pages + 1 physical pages to hold the partial data.
// The partial page will be padded to full physical page size.
(full_pages + 1) * physical_page_size
} else {
| // Update blob state and buffer based on the desired logical size. The page data is | ||
| // read with CRC validation, then durably rewritten below with a shorter CRC. | ||
| blob_guard.current_page = full_pages; | ||
| buf_guard.offset = full_pages * logical_page_size; |
d0ba636 to
b881c8a
Compare
b2e6e54 to
714471e
Compare
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## main #3837 +/- ##
==========================================
+ Coverage 95.73% 95.77% +0.03%
==========================================
Files 480 486 +6
Lines 197098 200130 +3032
Branches 4808 4856 +48
==========================================
+ Hits 188696 191669 +2973
- Misses 6803 6831 +28
- Partials 1599 1630 +31
... and 40 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|

Summary
Fix crash safety for
runtime::utils::buffer::paged::Append::resize()when shrinking a committed page to a shorter partial page, without changing the on-disk CRC format.The paged append layer stores two
(len, crc)slots per page. Becauselenis not covered by the CRC, shrink rewrites must be careful not to let a torn footer write fabricate a larger authoritative length or destroy the only recoverable footer. This PR makes partial-page shrink use a staged footer update so recovery observes either the old committed page or the new shorter page.Detailed Summary
partial_page_state.resize(current_size).len.This keeps the storage format unchanged. It hardens the torn-write states created by this implementation, but arbitrary length-field corruption remains a format-level limitation because
lenis still not included in the CRC.Tests
resize()to the current size as a no-op.Validated with:
cargo fmt -p commonware-runtime git diff --check -- runtime/src/utils/buffer/paged/append.rs runtime/src/utils/buffer/paged/mod.rs just test -p commonware-runtime utils::buffer::paged::append Towards: https://github.com/commonwarexyz/monorepo/issues/1432