aurora_evm/core/
memory.rs

1use super::prelude::*;
2use super::utils::USIZE_MAX;
3use crate::{ExitError, ExitFatal};
4use core::cmp::min;
5use core::ops::{BitAnd, Not};
6use primitive_types::{H256, U256};
7
8/// A sequential memory. It uses Rust's `Vec` for internal
9/// representation.
10#[derive(Clone, Debug)]
11pub struct Memory {
12    /// Memory data
13    data: Vec<u8>,
14    /// Memory effective length, that changed after resize operations.
15    effective_len: usize,
16    /// Memory limit
17    limit: usize,
18}
19
20impl Memory {
21    /// Create a new memory with the given limit.
22    #[must_use]
23    pub const fn new(limit: usize) -> Self {
24        Self {
25            data: Vec::new(),
26            effective_len: 0,
27            limit,
28        }
29    }
30
31    /// Memory limit.
32    #[must_use]
33    pub const fn limit(&self) -> usize {
34        self.limit
35    }
36
37    /// Get the length of the current memory range.
38    #[must_use]
39    pub fn len(&self) -> usize {
40        self.data.len()
41    }
42
43    /// Get the effective length.
44    #[must_use]
45    pub const fn effective_len(&self) -> usize {
46        self.effective_len
47    }
48
49    /// Return true if current effective memory range is zero.
50    #[must_use]
51    pub fn is_empty(&self) -> bool {
52        self.len() == 0
53    }
54
55    /// Return the full memory.
56    #[must_use]
57    pub const fn data(&self) -> &Vec<u8> {
58        &self.data
59    }
60
61    /// Resize the memory, making it cover the memory region of `offset..offset + len`,
62    /// with 32 bytes as the step. If the length is zero, this function does nothing.
63    ///
64    /// # Errors
65    /// Return `ExitError::InvalidRange` if `offset + len` is overflow.
66    pub fn resize_offset(&mut self, offset: usize, len: usize) -> Result<(), ExitError> {
67        if len == 0 {
68            return Ok(());
69        }
70
71        offset
72            .checked_add(len)
73            .map_or(Err(ExitError::InvalidRange), |end| self.resize_end(end))
74    }
75
76    /// Resize the memory, making it cover to `end`, with 32 bytes as the step.
77    ///
78    /// # Errors
79    /// Return `ExitError::InvalidRange` if `end` value is overflow in `next_multiple_of_32` call.
80    pub fn resize_end(&mut self, end: usize) -> Result<(), ExitError> {
81        if end > self.effective_len {
82            let new_end = next_multiple_of_32(end).ok_or(ExitError::InvalidRange)?;
83            self.effective_len = new_end;
84        }
85
86        Ok(())
87    }
88
89    /// Get memory region at given offset.
90    ///
91    /// ## Panics
92    ///
93    /// Value of `size` is considered trusted. If they're too large,
94    /// the program can run out of memory, or it can overflow.
95    #[must_use]
96    pub fn get(&self, mut offset: usize, size: usize) -> Vec<u8> {
97        if offset > self.data.len() {
98            offset = self.data.len();
99        }
100
101        let mut end = offset + size;
102        if end > self.data.len() {
103            end = self.data.len();
104        }
105
106        let mut ret = self.data[offset..end].to_vec();
107        ret.resize(size, 0);
108        ret
109    }
110
111    /// Get `H256` value from a specific offset in memory.
112    #[must_use]
113    pub fn get_h256(&self, offset: usize) -> H256 {
114        let mut ret = [0; 32];
115
116        #[allow(clippy::needless_range_loop)]
117        for index in 0..32 {
118            let position = offset + index;
119            if position >= self.data.len() {
120                break;
121            }
122
123            ret[index] = self.data[position];
124        }
125
126        H256(ret)
127    }
128
129    /// Set memory region at given offset. The offset and value is considered
130    /// untrusted.
131    ///
132    /// # Errors
133    /// Return `ExitFatal::NotSupported` if `offset + target_size` is out of memory limit or overflow.
134    pub fn set(
135        &mut self,
136        offset: usize,
137        value: &[u8],
138        target_size: Option<usize>,
139    ) -> Result<(), ExitFatal> {
140        let target_size = target_size.unwrap_or(value.len());
141        if target_size == 0 {
142            return Ok(());
143        }
144
145        if offset
146            .checked_add(target_size)
147            .map_or(true, |pos| pos > self.limit)
148        {
149            return Err(ExitFatal::NotSupported);
150        }
151
152        if self.data.len() < offset + target_size {
153            self.data.resize(offset + target_size, 0);
154        }
155
156        if target_size > value.len() {
157            self.data[offset..((value.len()) + offset)].clone_from_slice(value);
158            for index in (value.len())..target_size {
159                self.data[offset + index] = 0;
160            }
161        } else {
162            self.data[offset..(target_size + offset)].clone_from_slice(&value[..target_size]);
163        }
164
165        Ok(())
166    }
167
168    /// Copy memory region form `src` to `dst` with length.
169    /// `copy_within` uses `memmove` to avoid `DoS` attacks.
170    ///
171    /// # Errors
172    /// Return `ExitFatal::Other`:
173    /// - `OverflowOnCopy` if `offset + length` is overflow
174    /// - `OutOfGasOnCopy` if `offst_length` out of memory limit
175    pub fn copy(
176        &mut self,
177        src_offset: usize,
178        dst_offset: usize,
179        length: usize,
180    ) -> Result<(), ExitFatal> {
181        // If length is zero - do nothing
182        if length == 0 {
183            return Ok(());
184        }
185
186        // Get maximum offset
187        let offset = core::cmp::max(src_offset, dst_offset);
188        let offset_length = offset
189            .checked_add(length)
190            .ok_or_else(|| ExitFatal::Other(Cow::from("OverflowOnCopy")))?;
191        if offset_length > self.limit {
192            return Err(ExitFatal::Other(Cow::from("OutOfGasOnCopy")));
193        }
194
195        // Resize data memory
196        if self.data.len() < offset_length {
197            self.data.resize(offset_length, 0);
198        }
199
200        self.data
201            .copy_within(src_offset..src_offset + length, dst_offset);
202        Ok(())
203    }
204
205    /// Copy `data` into the memory, of given `len`.
206    ///
207    /// # Errors
208    /// Return `ExitFatal::NotSupported` if `set()` call return out of memory limit.
209    pub fn copy_large(
210        &mut self,
211        memory_offset: usize,
212        data_offset: U256,
213        len: usize,
214        data: &[u8],
215    ) -> Result<(), ExitFatal> {
216        // Needed to pass ethereum test defined in
217        // https://github.com/ethereum/tests/commit/17f7e7a6c64bb878c1b6af9dc8371b46c133e46d
218        // (regardless of other inputs, a zero-length copy is defined to be a no-op).
219        // TODO: refactor `set` and `copy_large` (see
220        // https://github.com/rust-blockchain/evm/pull/40#discussion_r677180794)
221        if len == 0 {
222            return Ok(());
223        }
224
225        #[allow(clippy::as_conversions)]
226        let data = data_offset
227            .checked_add(len.into())
228            .map_or(&[] as &[u8], |end| {
229                if end > USIZE_MAX {
230                    &[]
231                } else {
232                    let data_offset = data_offset.as_usize();
233                    let end = end.as_usize();
234
235                    if data_offset > data.len() {
236                        &[]
237                    } else {
238                        &data[data_offset..min(end, data.len())]
239                    }
240                }
241            });
242
243        self.set(memory_offset, data, Some(len))
244    }
245}
246
247/// Rounds up `x` to the closest multiple of 32. If `x % 32 == 0` then `x` is returned.
248#[inline]
249fn next_multiple_of_32(x: usize) -> Option<usize> {
250    let r = x.bitand(31).not().wrapping_add(1).bitand(31);
251    x.checked_add(r)
252}
253
254#[cfg(test)]
255mod tests {
256    use super::next_multiple_of_32;
257
258    #[test]
259    fn test_next_multiple_of_32() {
260        // next_multiple_of_32 returns x when it is a multiple of 32
261        for i in 0..32 {
262            let x = i * 32;
263            assert_eq!(Some(x), next_multiple_of_32(x));
264        }
265
266        // next_multiple_of_32 rounds up to the nearest multiple of 32 when `x % 32 != 0`
267        for x in 0..1024 {
268            if x % 32 == 0 {
269                continue;
270            }
271            let next_multiple = x + 32 - (x % 32);
272            assert_eq!(Some(next_multiple), next_multiple_of_32(x));
273        }
274
275        // next_multiple_of_32 returns None when the next multiple of 32 is too big
276        let last_multiple_of_32 = usize::MAX & !31;
277        for i in 0..63 {
278            let x = usize::MAX - i;
279            if x > last_multiple_of_32 {
280                assert_eq!(None, next_multiple_of_32(x));
281            } else {
282                assert_eq!(Some(last_multiple_of_32), next_multiple_of_32(x));
283            }
284        }
285    }
286}