diff --git a/crates/common/types/account.rs b/crates/common/types/account.rs index 589ac4f41c..e2195e1a60 100644 --- a/crates/common/types/account.rs +++ b/crates/common/types/account.rs @@ -55,6 +55,19 @@ impl Code { } } + /// Returns true iff `target` indexes a valid JUMPDEST in this bytecode. + /// + /// `jump_targets` is built by [`Self::compute_jump_targets`] and contains every + /// position whose byte is `JUMPDEST` (0x5B) and that is not inside a PUSH + /// literal; its entries are `u32` (bytecode length fits in `u32`). + /// + /// Uses `u32::try_from` rather than `target as u32` so that a `target` with + /// nonzero upper bits (e.g. `2^32 + k` for a real JUMPDEST at `k`) is + /// rejected instead of aliasing down to a valid-looking low-32-bit index. + pub fn is_valid_jump_target(&self, target: usize) -> bool { + u32::try_from(target).is_ok_and(|t| self.jump_targets.binary_search(&t).is_ok()) + } + fn compute_jump_targets(code: &[u8]) -> Vec { debug_assert!(code.len() <= u32::MAX as usize); let mut targets = Vec::new(); diff --git a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs index 7cf1fa89d0..6a895356a9 100644 --- a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs +++ b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs @@ -23,7 +23,6 @@ use crate::{ gas_cost::{self, SSTORE_STIPEND, STATE_GAS_STORAGE_SET}, memory::calculate_memory_size, opcode_handlers::OpcodeHandler, - opcodes::Opcode, utils::{size_offset_to_usize, u256_to_usize}, vm::VM, }; @@ -417,22 +416,7 @@ fn jump(vm: &mut VM<'_>, target: usize) -> Result<(), VMError> { // Check target address validity. // - Target bytecode has to be a JUMPDEST. // - Target address must not be blacklisted (aka. the JUMPDEST must not be part of a literal). - #[expect(clippy::as_conversions, reason = "safe")] - if vm - .current_call_frame - .bytecode - .bytecode - .get(target) - .is_some_and(|&value| { - value == Opcode::JUMPDEST as u8 - && vm - .current_call_frame - .bytecode - .jump_targets - .binary_search(&(target as u32)) - .is_ok() - }) - { + if vm.current_call_frame.bytecode.is_valid_jump_target(target) { // Update PC and skip the JUMPDEST instruction. vm.current_call_frame.pc = target.wrapping_add(1); vm.current_call_frame