revm_interpreter/interpreter/
shared_memory.rs

1use core::{cmp::min, fmt, ops::Range};
2use revm_primitives::{B256, U256};
3use std::vec::Vec;
4
5/// A sequential memory shared between calls, which uses
6/// a `Vec` for internal representation.
7/// A [SharedMemory] instance should always be obtained using
8/// the `new` static method to ensure memory safety.
9#[derive(Clone, PartialEq, Eq, Hash)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct SharedMemory {
12    /// The underlying buffer.
13    buffer: Vec<u8>,
14    /// Memory checkpoints for each depth.
15    /// Invariant: these are always in bounds of `data`.
16    checkpoints: Vec<usize>,
17    /// Invariant: equals `self.checkpoints.last()`
18    last_checkpoint: usize,
19    /// Memory limit. See [`CfgEnv`](revm_primitives::CfgEnv).
20    #[cfg(feature = "memory_limit")]
21    memory_limit: u64,
22}
23
24/// Empty shared memory.
25///
26/// Used as placeholder inside Interpreter when it is not running.
27pub const EMPTY_SHARED_MEMORY: SharedMemory = SharedMemory {
28    buffer: Vec::new(),
29    checkpoints: Vec::new(),
30    last_checkpoint: 0,
31    #[cfg(feature = "memory_limit")]
32    memory_limit: u64::MAX,
33};
34
35impl fmt::Debug for SharedMemory {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        f.debug_struct("SharedMemory")
38            .field("current_len", &self.len())
39            .field(
40                "context_memory",
41                &crate::primitives::hex::encode(self.context_memory()),
42            )
43            .finish_non_exhaustive()
44    }
45}
46
47impl Default for SharedMemory {
48    #[inline]
49    fn default() -> Self {
50        Self::new()
51    }
52}
53
54impl SharedMemory {
55    /// Creates a new memory instance that can be shared between calls.
56    ///
57    /// The default initial capacity is 4KiB.
58    #[inline]
59    pub fn new() -> Self {
60        Self::with_capacity(4 * 1024) // from evmone
61    }
62
63    /// Creates a new memory instance that can be shared between calls with the given `capacity`.
64    #[inline]
65    pub fn with_capacity(capacity: usize) -> Self {
66        Self {
67            buffer: Vec::with_capacity(capacity),
68            checkpoints: Vec::with_capacity(32),
69            last_checkpoint: 0,
70            #[cfg(feature = "memory_limit")]
71            memory_limit: u64::MAX,
72        }
73    }
74
75    /// Creates a new memory instance that can be shared between calls,
76    /// with `memory_limit` as upper bound for allocation size.
77    ///
78    /// The default initial capacity is 4KiB.
79    #[cfg(feature = "memory_limit")]
80    #[inline]
81    pub fn new_with_memory_limit(memory_limit: u64) -> Self {
82        Self {
83            memory_limit,
84            ..Self::new()
85        }
86    }
87
88    /// Returns `true` if the `new_size` for the current context memory will
89    /// make the shared buffer length exceed the `memory_limit`.
90    #[cfg(feature = "memory_limit")]
91    #[inline]
92    pub fn limit_reached(&self, new_size: usize) -> bool {
93        self.last_checkpoint.saturating_add(new_size) as u64 > self.memory_limit
94    }
95
96    /// Prepares the shared memory for a new context.
97    #[inline]
98    pub fn new_context(&mut self) {
99        let new_checkpoint = self.buffer.len();
100        self.checkpoints.push(new_checkpoint);
101        self.last_checkpoint = new_checkpoint;
102    }
103
104    /// Prepares the shared memory for returning to the previous context.
105    #[inline]
106    pub fn free_context(&mut self) {
107        if let Some(old_checkpoint) = self.checkpoints.pop() {
108            self.last_checkpoint = self.checkpoints.last().cloned().unwrap_or_default();
109            // SAFETY: buffer length is less than or equal `old_checkpoint`
110            unsafe { self.buffer.set_len(old_checkpoint) };
111        }
112    }
113
114    /// Returns the length of the current memory range.
115    #[inline]
116    pub fn len(&self) -> usize {
117        self.buffer.len() - self.last_checkpoint
118    }
119
120    /// Returns `true` if the current memory range is empty.
121    #[inline]
122    pub fn is_empty(&self) -> bool {
123        self.len() == 0
124    }
125
126    /// Returns the gas cost for the current memory expansion.
127    #[inline]
128    pub fn current_expansion_cost(&self) -> u64 {
129        crate::gas::memory_gas_for_len(self.len())
130    }
131
132    /// Resizes the memory in-place so that `len` is equal to `new_len`.
133    #[inline]
134    pub fn resize(&mut self, new_size: usize) {
135        self.buffer.resize(self.last_checkpoint + new_size, 0);
136    }
137
138    /// Returns a byte slice of the memory region at the given offset.
139    ///
140    /// # Panics
141    ///
142    /// Panics on out of bounds.
143    #[inline]
144    #[cfg_attr(debug_assertions, track_caller)]
145    pub fn slice(&self, offset: usize, size: usize) -> &[u8] {
146        self.slice_range(offset..offset + size)
147    }
148
149    /// Returns a byte slice of the memory region at the given offset.
150    ///
151    /// # Panics
152    ///
153    /// Panics on out of bounds.
154    #[inline]
155    #[cfg_attr(debug_assertions, track_caller)]
156    pub fn slice_range(&self, range @ Range { start, end }: Range<usize>) -> &[u8] {
157        match self.context_memory().get(range) {
158            Some(slice) => slice,
159            None => debug_unreachable!("slice OOB: {start}..{end}; len: {}", self.len()),
160        }
161    }
162
163    /// Returns a byte slice of the memory region at the given offset.
164    ///
165    /// # Panics
166    ///
167    /// Panics on out of bounds.
168    #[inline]
169    #[cfg_attr(debug_assertions, track_caller)]
170    pub fn slice_mut(&mut self, offset: usize, size: usize) -> &mut [u8] {
171        let end = offset + size;
172        match self.context_memory_mut().get_mut(offset..end) {
173            Some(slice) => slice,
174            None => debug_unreachable!("slice OOB: {offset}..{end}"),
175        }
176    }
177
178    /// Returns the byte at the given offset.
179    ///
180    /// # Panics
181    ///
182    /// Panics on out of bounds.
183    #[inline]
184    pub fn get_byte(&self, offset: usize) -> u8 {
185        self.slice(offset, 1)[0]
186    }
187
188    /// Returns a 32-byte slice of the memory region at the given offset.
189    ///
190    /// # Panics
191    ///
192    /// Panics on out of bounds.
193    #[inline]
194    pub fn get_word(&self, offset: usize) -> B256 {
195        self.slice(offset, 32).try_into().unwrap()
196    }
197
198    /// Returns a U256 of the memory region at the given offset.
199    ///
200    /// # Panics
201    ///
202    /// Panics on out of bounds.
203    #[inline]
204    pub fn get_u256(&self, offset: usize) -> U256 {
205        self.get_word(offset).into()
206    }
207
208    /// Sets the `byte` at the given `index`.
209    ///
210    /// # Panics
211    ///
212    /// Panics on out of bounds.
213    #[inline]
214    #[cfg_attr(debug_assertions, track_caller)]
215    pub fn set_byte(&mut self, offset: usize, byte: u8) {
216        self.set(offset, &[byte]);
217    }
218
219    /// Sets the given 32-byte `value` to the memory region at the given `offset`.
220    ///
221    /// # Panics
222    ///
223    /// Panics on out of bounds.
224    #[inline]
225    #[cfg_attr(debug_assertions, track_caller)]
226    pub fn set_word(&mut self, offset: usize, value: &B256) {
227        self.set(offset, &value[..]);
228    }
229
230    /// Sets the given U256 `value` to the memory region at the given `offset`.
231    ///
232    /// # Panics
233    ///
234    /// Panics on out of bounds.
235    #[inline]
236    #[cfg_attr(debug_assertions, track_caller)]
237    pub fn set_u256(&mut self, offset: usize, value: U256) {
238        self.set(offset, &value.to_be_bytes::<32>());
239    }
240
241    /// Set memory region at given `offset`.
242    ///
243    /// # Panics
244    ///
245    /// Panics on out of bounds.
246    #[inline]
247    #[cfg_attr(debug_assertions, track_caller)]
248    pub fn set(&mut self, offset: usize, value: &[u8]) {
249        if !value.is_empty() {
250            self.slice_mut(offset, value.len()).copy_from_slice(value);
251        }
252    }
253
254    /// Set memory from data. Our memory offset+len is expected to be correct but we
255    /// are doing bound checks on data/data_offeset/len and zeroing parts that is not copied.
256    ///
257    /// # Panics
258    ///
259    /// Panics if memory is out of bounds.
260    #[inline]
261    #[cfg_attr(debug_assertions, track_caller)]
262    pub fn set_data(&mut self, memory_offset: usize, data_offset: usize, len: usize, data: &[u8]) {
263        if data_offset >= data.len() {
264            // nullify all memory slots
265            self.slice_mut(memory_offset, len).fill(0);
266            return;
267        }
268        let data_end = min(data_offset + len, data.len());
269        let data_len = data_end - data_offset;
270        debug_assert!(data_offset < data.len() && data_end <= data.len());
271        let data = unsafe { data.get_unchecked(data_offset..data_end) };
272        self.slice_mut(memory_offset, data_len)
273            .copy_from_slice(data);
274
275        // nullify rest of memory slots
276        // SAFETY: Memory is assumed to be valid, and it is commented where this assumption is made.
277        self.slice_mut(memory_offset + data_len, len - data_len)
278            .fill(0);
279    }
280
281    /// Copies elements from one part of the memory to another part of itself.
282    ///
283    /// # Panics
284    ///
285    /// Panics on out of bounds.
286    #[inline]
287    #[cfg_attr(debug_assertions, track_caller)]
288    pub fn copy(&mut self, dst: usize, src: usize, len: usize) {
289        self.context_memory_mut().copy_within(src..src + len, dst);
290    }
291
292    /// Returns a reference to the memory of the current context, the active memory.
293    #[inline]
294    pub fn context_memory(&self) -> &[u8] {
295        // SAFETY: access bounded by buffer length
296        unsafe {
297            self.buffer
298                .get_unchecked(self.last_checkpoint..self.buffer.len())
299        }
300    }
301
302    /// Returns a mutable reference to the memory of the current context.
303    #[inline]
304    pub fn context_memory_mut(&mut self) -> &mut [u8] {
305        let buf_len = self.buffer.len();
306        // SAFETY: access bounded by buffer length
307        unsafe { self.buffer.get_unchecked_mut(self.last_checkpoint..buf_len) }
308    }
309}
310
311/// Returns number of words what would fit to provided number of bytes,
312/// i.e. it rounds up the number bytes to number of words.
313#[inline]
314pub const fn num_words(len: u64) -> u64 {
315    len.saturating_add(31) / 32
316}
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321
322    #[test]
323    fn test_num_words() {
324        assert_eq!(num_words(0), 0);
325        assert_eq!(num_words(1), 1);
326        assert_eq!(num_words(31), 1);
327        assert_eq!(num_words(32), 1);
328        assert_eq!(num_words(33), 2);
329        assert_eq!(num_words(63), 2);
330        assert_eq!(num_words(64), 2);
331        assert_eq!(num_words(65), 3);
332        assert_eq!(num_words(u64::MAX), u64::MAX / 32);
333    }
334
335    #[test]
336    fn new_free_context() {
337        let mut shared_memory = SharedMemory::new();
338        shared_memory.new_context();
339
340        assert_eq!(shared_memory.buffer.len(), 0);
341        assert_eq!(shared_memory.checkpoints.len(), 1);
342        assert_eq!(shared_memory.last_checkpoint, 0);
343
344        unsafe { shared_memory.buffer.set_len(32) };
345        assert_eq!(shared_memory.len(), 32);
346        shared_memory.new_context();
347
348        assert_eq!(shared_memory.buffer.len(), 32);
349        assert_eq!(shared_memory.checkpoints.len(), 2);
350        assert_eq!(shared_memory.last_checkpoint, 32);
351        assert_eq!(shared_memory.len(), 0);
352
353        unsafe { shared_memory.buffer.set_len(96) };
354        assert_eq!(shared_memory.len(), 64);
355        shared_memory.new_context();
356
357        assert_eq!(shared_memory.buffer.len(), 96);
358        assert_eq!(shared_memory.checkpoints.len(), 3);
359        assert_eq!(shared_memory.last_checkpoint, 96);
360        assert_eq!(shared_memory.len(), 0);
361
362        // free contexts
363        shared_memory.free_context();
364        assert_eq!(shared_memory.buffer.len(), 96);
365        assert_eq!(shared_memory.checkpoints.len(), 2);
366        assert_eq!(shared_memory.last_checkpoint, 32);
367        assert_eq!(shared_memory.len(), 64);
368
369        shared_memory.free_context();
370        assert_eq!(shared_memory.buffer.len(), 32);
371        assert_eq!(shared_memory.checkpoints.len(), 1);
372        assert_eq!(shared_memory.last_checkpoint, 0);
373        assert_eq!(shared_memory.len(), 32);
374
375        shared_memory.free_context();
376        assert_eq!(shared_memory.buffer.len(), 0);
377        assert_eq!(shared_memory.checkpoints.len(), 0);
378        assert_eq!(shared_memory.last_checkpoint, 0);
379        assert_eq!(shared_memory.len(), 0);
380    }
381
382    #[test]
383    fn resize() {
384        let mut shared_memory = SharedMemory::new();
385        shared_memory.new_context();
386
387        shared_memory.resize(32);
388        assert_eq!(shared_memory.buffer.len(), 32);
389        assert_eq!(shared_memory.len(), 32);
390        assert_eq!(shared_memory.buffer.get(0..32), Some(&[0_u8; 32] as &[u8]));
391
392        shared_memory.new_context();
393        shared_memory.resize(96);
394        assert_eq!(shared_memory.buffer.len(), 128);
395        assert_eq!(shared_memory.len(), 96);
396        assert_eq!(
397            shared_memory.buffer.get(32..128),
398            Some(&[0_u8; 96] as &[u8])
399        );
400
401        shared_memory.free_context();
402        shared_memory.resize(64);
403        assert_eq!(shared_memory.buffer.len(), 64);
404        assert_eq!(shared_memory.len(), 64);
405        assert_eq!(shared_memory.buffer.get(0..64), Some(&[0_u8; 64] as &[u8]));
406    }
407}