1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use super::*;
impl ExecutionContext {
/// Return the length of the calldata buffer
pub fn input_size(&self) -> usize {
self.input_data.len()
}
/// Read a slice of calldata, zero-padding when the requested range exceeds the buffer.
///
/// Returns an error if `length` exceeds `memory_limit`. Without this guard,
/// any future opcode that wires `calldatacopy` / `copy_input_to_memory` through
/// to user-controlled length would inherit the bug-#15 OOM-DoS pattern (the
/// `vec![0u8; length]` would allocate before `write_memory` got a chance to
/// reject the request). Mirrors the PUSHDATA4 length check.
pub fn read_input_slice(&self, offset: usize, length: usize) -> Result<Vec<u8>, RuntimeError> {
if length > self.memory_limit {
return Err(RuntimeError::ExecutionError {
message: format!(
"calldata read length {} exceeds memory limit {}",
length, self.memory_limit
),
});
}
let mut buffer = vec![0u8; length];
if offset >= self.input_data.len() {
return Ok(buffer);
}
let end = offset
.checked_add(length)
.ok_or(RuntimeError::ExecutionError {
message: "calldata read range overflow".to_string(),
})?;
let available = end.min(self.input_data.len());
let copy_len = available.saturating_sub(offset);
buffer[..copy_len].copy_from_slice(&self.input_data[offset..offset + copy_len]);
Ok(buffer)
}
/// Copy calldata into linear memory, expanding memory as required
pub fn copy_input_to_memory(
&mut self,
memory_offset: usize,
input_offset: usize,
length: usize,
) -> Result<(), RuntimeError> {
if length == 0 {
return Ok(());
}
let data = self.read_input_slice(input_offset, length)?;
self.write_memory(memory_offset, &data)
}
/// Size of the calldata buffer
pub fn calldatasize(&self) -> usize {
self.input_data.len()
}
/// Load a 32-byte word from calldata starting at `offset`
pub fn calldataload_word(&self, offset: usize) -> [u8; 32] {
// 32 is a compile-time constant well under any reasonable memory_limit;
// unwrap is safe.
let data = self
.read_input_slice(offset, 32)
.expect("calldataload_word: 32-byte read cannot exceed memory_limit");
let mut word = [0u8; 32];
word.copy_from_slice(&data);
word
}
/// Copy calldata into linear memory
pub fn calldatacopy(
&mut self,
memory_offset: usize,
data_offset: usize,
length: usize,
) -> Result<(), RuntimeError> {
if length == 0 {
return Ok(());
}
let data = self.read_input_slice(data_offset, length)?;
self.write_memory(memory_offset, &data)
}
}