From 77aeb889aed4c9de6eb8ba7c02ef7ff2a963e0de Mon Sep 17 00:00:00 2001 From: krishvishal Date: Sat, 8 Mar 2025 09:30:07 +0530 Subject: [PATCH] Add loop termination condition when `pc = 0` in `find_free_cell`. The issue is caused by the function try to read from non-existent free blocks. --- core/storage/btree.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/core/storage/btree.rs b/core/storage/btree.rs index 0069a8f76..9330c81c9 100644 --- a/core/storage/btree.rs +++ b/core/storage/btree.rs @@ -2324,6 +2324,7 @@ fn find_free_cell(page_ref: &PageContent, usable_space: u16, amount: usize) -> R while pc <= maxpc { let next = u16::from_be_bytes(buf[pc..pc + 2].try_into().unwrap()); let size = u16::from_be_bytes(buf[pc + 2..pc + 4].try_into().unwrap()); + println!("size after reading = {}", size); if amount <= size as usize { if amount == size as usize { // delete whole thing @@ -2331,6 +2332,8 @@ fn find_free_cell(page_ref: &PageContent, usable_space: u16, amount: usize) -> R } else { // take only the part we are interested in by reducing the size let new_size = size - amount as u16; + println!("size = {}", size); + println!("amount = {}", amount); // size includes 4 bytes of freeblock // we need to leave the free block at least if new_size >= 4 { @@ -2341,14 +2344,19 @@ fn find_free_cell(page_ref: &PageContent, usable_space: u16, amount: usize) -> R let frag = page_ref.num_frag_free_bytes() + new_size as u8; page_ref.write_u8(PAGE_HEADER_OFFSET_FRAGMENTED_BYTES_COUNT, frag); } + println!("find_free_cell new_size = {}", new_size); pc += new_size as usize; } + println!("find_free_cell pc = {}", pc); return Ok(pc); } prev_pc = pc; pc = next as usize; - if pc <= prev_pc && pc != 0 { - return_corrupt!("Free list not in ascending order"); + if pc <= prev_pc { + if pc != 0 { + return_corrupt!("Free list not in ascending order"); + } + return Ok(0); } } if pc > maxpc + amount - 4 { @@ -2523,8 +2531,19 @@ fn free_cell_range( len: u16, usable_space: u16, ) -> Result<()> { + println!("Before free_cell_range(offset={}, len={})", offset, len); + + if len < 4 { + return_corrupt!("Minimum cell size is 4"); + } + + if offset > usable_space.saturating_sub(4) { + return_corrupt!("Start offset beyond usable space"); + } + let mut size = len; let mut end = offset + len; + println!("free_cell_range end = {}", end); let mut pointer_to_pc = page.offset as u16 + 1; // if the freeblock list is empty, we set this block as the first freeblock in the page header. let pc = if page.first_freeblock() == 0 { @@ -2602,6 +2621,8 @@ fn free_cell_range( page.write_u16_no_offset(offset as usize, pc); page.write_u16_no_offset(offset as usize + 2, size); } + println!("After free_cell_range"); + Ok(()) } @@ -3925,12 +3946,16 @@ mod tests { let usable_space = 4096; let mut i = 1000; let seed = thread_rng().gen(); + // let seed = 15292777653676891381; + println!("SEED = {}", seed); tracing::info!("seed {}", seed); let mut rng = ChaCha8Rng::seed_from_u64(seed); while i > 0 { i -= 1; match rng.next_u64() % 3 { 0 => { + println!("#######################"); + println!("INSERT"); // allow appends with extra place to insert let cell_idx = rng.next_u64() as usize % (page.cell_count() + 1); let free = compute_free_space(page, usable_space); @@ -3954,6 +3979,8 @@ mod tests { cells.push(Cell { pos: i, payload }); } 1 => { + println!("#######################"); + println!("DROP CELL"); if page.cell_count() == 0 { continue; } @@ -3969,12 +3996,15 @@ mod tests { cells.remove(cell_idx); } 2 => { + println!("#######################"); + println!("DEFRAG PAGE"); defragment_page(page, usable_space); } _ => unreachable!(), } let free = compute_free_space(page, usable_space); assert_eq!(free, 4096 - total_size - header_size); + println!("SEED = {}", seed); } }