@@ -798,8 +798,9 @@ impl<B: Blob> Append<B> {
798798 ///
799799 /// Since larger valid lengths are authoritative, a shorter CRC cannot simply be written next to
800800 /// the old CRC. We first stage the shorter slot with length 0, then make its length durable,
801- /// then invalidate the old longer slot. A crash during any phase recovers either the old longer
802- /// page or the new shorter page, but never loses the whole page or fabricates a larger length.
801+ /// then clear the old slot's CRC bytes before clearing its length bytes. A crash during any
802+ /// phase recovers either the old longer page or the new shorter page, but never loses the whole
803+ /// page or fabricates a larger length.
803804 async fn sync_partial_page_shrink (
804805 blob : & B ,
805806 page : u64 ,
@@ -821,34 +822,36 @@ impl<B: Blob> Append<B> {
821822 } else {
822823 0
823824 } ;
825+ let new_slot_offset = crc_start
826+ . checked_add ( new_slot_start as u64 )
827+ . ok_or ( Error :: OffsetOverflow ) ?;
824828 let staged_slot = Self :: checksum_slot_bytes ( 0 , new_crc) ;
825- blob. write_at ( crc_start + new_slot_start as u64 , staged_slot. to_vec ( ) )
826- . await ?;
829+ blob. write_at ( new_slot_offset, staged_slot. to_vec ( ) ) . await ?;
827830 blob. sync ( ) . await ?;
828831
829- blob. write_at (
830- crc_start + new_slot_start as u64 ,
831- new_len. to_be_bytes ( ) . to_vec ( ) ,
832- )
833- . await ?;
832+ blob. write_at ( new_slot_offset, new_len. to_be_bytes ( ) . to_vec ( ) )
833+ . await ?;
834834 blob. sync ( ) . await ?;
835835
836836 let old_slot_start = if new_slot_start == 0 {
837837 CHECKSUM_SLOT_SIZE
838838 } else {
839839 0
840840 } ;
841+ let old_slot_offset = crc_start
842+ . checked_add ( old_slot_start as u64 )
843+ . ok_or ( Error :: OffsetOverflow ) ?;
841844 let len_size = std:: mem:: size_of :: < u16 > ( ) ;
842845 let crc_size = CHECKSUM_SLOT_SIZE - len_size;
843- blob. write_at (
844- crc_start + old_slot_start as u64 + len_size as u64 ,
845- vec ! [ 0u8 ; crc_size] ,
846- )
847- . await ?;
846+ let old_crc_offset = old_slot_offset
847+ . checked_add ( len_size as u64 )
848+ . ok_or ( Error :: OffsetOverflow ) ?;
849+
850+ // Clear CRC bytes before length bytes so torn invalidation cannot create a new valid slot.
851+ blob. write_at ( old_crc_offset, vec ! [ 0u8 ; crc_size] ) . await ?;
848852 blob. sync ( ) . await ?;
849853
850- blob. write_at ( crc_start + old_slot_start as u64 , vec ! [ 0u8 ; len_size] )
851- . await ?;
854+ blob. write_at ( old_slot_offset, vec ! [ 0u8 ; len_size] ) . await ?;
852855 blob. sync ( ) . await ?;
853856
854857 let final_record = if new_slot_start == 0 {
0 commit comments