Skip to content

Commit 8ab56bc

Browse files
authored
fix(levm): fix problems related to 32 bit architecture (#4104)
**Motivation** - Find errors on 32 bit architecture **Description** <!-- A clear and concise general description of the changes this PR introduces --> - `u256_to_usize` now halts if the number doesn't fit in 32 bits instead of 64. - Memory size cost is computed using u64 instead of usize. The previous implementation overflowed on 32 bit architectures in some edge cases. Also I made it prettier :D How this was tested: Adapting ethrex to run on 32 bit x86 in branch [replay_32_bit_x86_testing](https://github.com/lambdaclass/ethrex/tree/replay_32_bit_x86_testing) and running EFTests, both state and blockchain. After these changes they all pass. <!-- Link to issues: Resolves #111, Resolves #222 --> Makes progress towards #4081
1 parent 96d1b07 commit 8ab56bc

File tree

3 files changed

+17
-20
lines changed

3 files changed

+17
-20
lines changed

crates/vm/levm/src/constants.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub const EMPTY_CODE_HASH: H256 = H256([
1515
0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70,
1616
]);
1717

18-
pub const MEMORY_EXPANSION_QUOTIENT: usize = 512;
18+
pub const MEMORY_EXPANSION_QUOTIENT: u64 = 512;
1919

2020
// Dedicated gas limit for system calls according to EIPs 2935, 4788, 7002 and 7251
2121
pub const SYS_CALL_GAS_LIMIT: u64 = 30000000;

crates/vm/levm/src/memory.rs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use std::{cell::RefCell, rc::Rc};
22

33
use crate::{
4-
constants::{MEMORY_EXPANSION_QUOTIENT, WORD_SIZE_IN_BYTES_USIZE},
4+
constants::{MEMORY_EXPANSION_QUOTIENT, WORD_SIZE_IN_BYTES_U64, WORD_SIZE_IN_BYTES_USIZE},
55
errors::{ExceptionalHalt, InternalError, VMError},
66
};
77
use ExceptionalHalt::OutOfBounds;
8-
use ExceptionalHalt::OutOfGas;
98
use ethrex_common::{
109
U256,
1110
utils::{u256_from_big_endian_const, u256_to_big_endian},
@@ -279,23 +278,20 @@ pub fn expansion_cost(new_memory_size: usize, current_memory_size: usize) -> Res
279278
}
280279

281280
/// The total cost for a given memory size.
281+
/// Gas cost should always be computed in u64
282282
#[inline]
283283
fn cost(memory_size: usize) -> Result<u64, VMError> {
284-
let memory_size_word = memory_size
285-
.checked_add(WORD_SIZE_IN_BYTES_USIZE.wrapping_sub(1))
286-
.ok_or(OutOfGas)?
287-
/ WORD_SIZE_IN_BYTES_USIZE;
288-
289-
let gas_cost = (memory_size_word
290-
.checked_mul(memory_size_word)
291-
.ok_or(OutOfGas)?
292-
/ MEMORY_EXPANSION_QUOTIENT)
293-
.checked_add(3usize.checked_mul(memory_size_word).ok_or(OutOfGas)?)
294-
.ok_or(OutOfGas)?;
295-
296-
gas_cost
297-
.try_into()
298-
.map_err(|_| ExceptionalHalt::VeryLargeNumber.into())
284+
let memory_size = u64::try_from(memory_size).map_err(|_| InternalError::TypeConversion)?;
285+
286+
// memory size measured in 32 byte words
287+
let words = memory_size.div_ceil(WORD_SIZE_IN_BYTES_U64);
288+
289+
// Cost(words) ≈ floor(words^2 / q) + 3 * words
290+
// For this to overflow memory size in words should be 2^32, which is impossible.
291+
#[expect(clippy::arithmetic_side_effects)]
292+
let gas_cost = words * words / MEMORY_EXPANSION_QUOTIENT + 3 * words;
293+
294+
Ok(gas_cost)
299295
}
300296

301297
#[inline]

crates/vm/levm/src/utils.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -680,10 +680,11 @@ pub fn account_to_levm_account(account: Account) -> (LevmAccount, Bytes) {
680680
)
681681
}
682682

683-
/// Converts a U256 value into usize, fails if the value is over 64 bytes
683+
/// Converts a U256 value into usize, returning an error if the value is over 32 bits
684+
/// This is generally used for memory offsets and sizes, 32 bits is more than enough for this purpose.
684685
#[expect(clippy::as_conversions)]
685686
pub fn u256_to_usize(val: U256) -> Result<usize, VMError> {
686-
if val.0[1] != 0 || val.0[2] != 0 || val.0[3] != 0 {
687+
if val.0[0] > u32::MAX as u64 || val.0[1] != 0 || val.0[2] != 0 || val.0[3] != 0 {
687688
return Err(VMError::ExceptionalHalt(ExceptionalHalt::VeryLargeNumber));
688689
}
689690
Ok(val.0[0] as usize)

0 commit comments

Comments
 (0)