Context: crates/lean_vm/src/isa/operands/mem_or_constant.rs
Description
The MemOrConstant computes addresses as fp + offset using normal usize addition. In optimized (release) builds, usize addition wraps on overflow.
If an attacker can influence fp or offset by providing malicious program state, they can cause fp + offset to wrap to a small index and read from or compute a pointer to an unintended memory cell. This can break call-frame isolation assumptions and, in parallel execution, may allow a segment to read from the shared region instead of faulting as UndefinedMemory, potentially impacting correctness, exposing data that should be out-of-scope for that frame.
Attack path
- Attacker supplies bytecode with a very large
offset or manipulates fp via Jump.updated_fp such that fp + offset overflows usize.
- VM executes
read_value() / memory_address() and the wrapped address points into a lower, valid memory region.
- The program reads or writes memory at this unintended address, possibly leaking or corrupting data.
Impact depends on whether bytecode is untrusted and whether memory layout contains sensitive data, at minimum it is a correctness/safety issue and can be leveraged as a denial-of-service or data integrity violation.
pub fn read_value(&self, memory: &impl MemoryAccess, fp: usize) -> Result<F, RunnerError> {
match self {
Self::Constant(c) => Ok(*c),
Self::MemoryAfterFp { offset } => memory.get(fp + *offset),
}
}
pub const fn memory_address(&self, fp: usize) -> Result<usize, RunnerError> {
match self {
Self::Constant(_) => Err(RunnerError::NotAPointer),
Self::MemoryAfterFp { offset } => Ok(fp + *offset),
}
}
Recommendation
Use checked arithmetic when forming addresses and fail safely on overflow, e.g. fp.checked_add(*offset).ok_or(RunnerError::OutOfMemory /* or new Overflow */)?; in both read_value() and memory_address().
- Consider also validating that offsets stay within the current frame size if frame isolation is intended.
Context:
crates/lean_vm/src/isa/operands/mem_or_constant.rsDescription
The
MemOrConstantcomputes addresses asfp + offsetusing normalusizeaddition. In optimized (release) builds,usizeaddition wraps on overflow.If an attacker can influence
fporoffsetby providing malicious program state, they can causefp + offsetto wrap to a small index and read from or compute a pointer to an unintended memory cell. This can break call-frame isolation assumptions and, in parallel execution, may allow a segment to read from the shared region instead of faulting asUndefinedMemory, potentially impacting correctness, exposing data that should be out-of-scope for that frame.Attack path
offsetor manipulatesfpviaJump.updated_fpsuch thatfp + offsetoverflowsusize.read_value()/memory_address()and the wrapped address points into a lower, valid memory region.Impact depends on whether bytecode is untrusted and whether memory layout contains sensitive data, at minimum it is a correctness/safety issue and can be leveraged as a denial-of-service or data integrity violation.
Recommendation
Use checked arithmetic when forming addresses and fail safely on overflow, e.g.
fp.checked_add(*offset).ok_or(RunnerError::OutOfMemory /* or new Overflow */)?;in bothread_value()andmemory_address().