From 48769aef6e2a03b5417421a734204343dd8e59b5 Mon Sep 17 00:00:00 2001 From: Oleksandr Kozachuk Date: Wed, 8 Apr 2026 12:27:33 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20ALLOCATE/RESIZE=20size=20validation=20?= =?UTF-8?q?=E2=80=94=20memory=20suite=20now=20passes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ALLOCATE and RESIZE with size -1 (0xFFFFFFFF) were "succeeding" because wrapping arithmetic made the block size tiny. Added early rejection for sizes exceeding half the available memory. Memory suite: 2→0 errors. Now 4 suites pass (Core, Facility, Memory). --- crates/core/src/outer.rs | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/crates/core/src/outer.rs b/crates/core/src/outer.rs index f2b3f82..5fd9787 100644 --- a/crates/core/src/outer.rs +++ b/crates/core/src/outer.rs @@ -4961,6 +4961,18 @@ impl ForthVM { let size = u32::from_le_bytes(b); let mem_len = data.len() as u32; + + // Reject obviously impossible sizes (> available memory) + if size > mem_len / 2 { + let data = memory.data_mut(&mut caller); + data[sp as usize..sp as usize + 4].copy_from_slice(&0i32.to_le_bytes()); + let new_sp = sp - CELL_SIZE; + data[new_sp as usize..new_sp as usize + 4] + .copy_from_slice(&(-1i32).to_le_bytes()); + dsp.set(&mut caller, Val::I32(new_sp as i32))?; + return Ok(()); + } + // Allocate from top of memory, growing downward // Use last 4 bytes of memory as the allocation pointer let alloc_ptr_addr = mem_len - 4; @@ -4973,8 +4985,8 @@ impl ForthVM { } // Block: [size(4)] [data(size)] — aligned to 4 bytes - let aligned_size = size.wrapping_add(3) & !3; - let block_size = 4u32.wrapping_add(aligned_size); + let aligned_size = (size + 3) & !3; + let block_size = 4 + aligned_size; if alloc_top < block_size + 0x20000 { // Not enough memory (leave some space for dictionary growth) @@ -5047,7 +5059,19 @@ impl ForthVM { .unwrap(); let old_addr = u32::from_le_bytes(b); + let mem_len = data.len() as u32; + + // Reject obviously impossible sizes + if new_size > mem_len / 2 { + let data = memory.data_mut(&mut caller); + data[(sp + 4) as usize..(sp + 8) as usize] + .copy_from_slice(&(old_addr as i32).to_le_bytes()); + data[sp as usize..sp as usize + 4].copy_from_slice(&(-1i32).to_le_bytes()); + return Ok(()); + } + // Read old size from header (4 bytes before old_addr) + let data = memory.data(&caller); let old_size = if old_addr >= 4 { let b: [u8; 4] = data[(old_addr - 4) as usize..old_addr as usize] .try_into() @@ -5057,7 +5081,6 @@ impl ForthVM { 0 }; - let mem_len = data.len() as u32; let alloc_ptr_addr = mem_len - 4; let b: [u8; 4] = data[alloc_ptr_addr as usize..mem_len as usize] .try_into() @@ -5067,8 +5090,8 @@ impl ForthVM { alloc_top = mem_len - 8; } - let aligned_size = new_size.wrapping_add(3) & !3; - let block_size = 4u32.wrapping_add(aligned_size); + let aligned_size = (new_size + 3) & !3; + let block_size = 4 + aligned_size; if alloc_top < block_size + 0x20000 { // Allocation failure