1use {
2 crate::config::ExecutionCost,
3 sbpf_vm::{
4 compute::ComputeMeter,
5 errors::{SbpfVmError, SbpfVmResult},
6 memory::Memory,
7 },
8};
9
10fn mem_op_consume(n: u64, compute: &ComputeMeter, costs: &ExecutionCost) -> SbpfVmResult<()> {
11 let cost = costs
12 .mem_op_base_cost
13 .max(n.checked_div(costs.cpi_bytes_per_unit).unwrap_or(u64::MAX));
14 compute.consume(cost)
15}
16
17fn is_nonoverlapping(src: u64, src_len: u64, dst: u64, dst_len: u64) -> bool {
18 if src > dst {
19 src.saturating_sub(dst) >= dst_len
20 } else {
21 dst.saturating_sub(src) >= src_len
22 }
23}
24
25pub fn sol_memcpy(
26 registers: [u64; 5],
27 memory: &mut Memory,
28 compute: &ComputeMeter,
29 costs: &ExecutionCost,
30) -> SbpfVmResult<u64> {
31 let dst = registers[0];
32 let src = registers[1];
33 let n = registers[2];
34
35 mem_op_consume(n, compute, costs)?;
36
37 if !is_nonoverlapping(src, n, dst, n) {
38 return Err(SbpfVmError::OverlappingMemoryRegions);
39 }
40
41 let data = memory.read_bytes(src, n as usize)?.to_vec();
42 memory.write_bytes(dst, &data)?;
43 Ok(0)
44}
45
46pub fn sol_memmove(
47 registers: [u64; 5],
48 memory: &mut Memory,
49 compute: &ComputeMeter,
50 costs: &ExecutionCost,
51) -> SbpfVmResult<u64> {
52 let dst = registers[0];
53 let src = registers[1];
54 let n = registers[2];
55
56 mem_op_consume(n, compute, costs)?;
57
58 let data = memory.read_bytes(src, n as usize)?.to_vec();
59 memory.write_bytes(dst, &data)?;
60 Ok(0)
61}
62
63pub fn sol_memset(
64 registers: [u64; 5],
65 memory: &mut Memory,
66 compute: &ComputeMeter,
67 costs: &ExecutionCost,
68) -> SbpfVmResult<u64> {
69 let dst = registers[0];
70 let c = registers[1] as u8;
71 let n = registers[2];
72
73 mem_op_consume(n, compute, costs)?;
74
75 let data = vec![c; n as usize];
76 memory.write_bytes(dst, &data)?;
77 Ok(0)
78}
79
80pub fn sol_memcmp(
81 registers: [u64; 5],
82 memory: &mut Memory,
83 compute: &ComputeMeter,
84 costs: &ExecutionCost,
85) -> SbpfVmResult<u64> {
86 let s1 = registers[0];
87 let s2 = registers[1];
88 let n = registers[2];
89 let result_ptr = registers[3];
90
91 mem_op_consume(n, compute, costs)?;
92
93 let s1_bytes = memory.read_bytes(s1, n as usize)?;
94 let s2_bytes = memory.read_bytes(s2, n as usize)?;
95
96 let mut result: i32 = 0;
97 for i in 0..n as usize {
98 if s1_bytes[i] != s2_bytes[i] {
99 result = (s1_bytes[i] as i32).saturating_sub(s2_bytes[i] as i32);
100 break;
101 }
102 }
103
104 memory.write_u32(result_ptr, result as u32)?;
105 Ok(0)
106}
107
108#[cfg(test)]
109mod tests {
110 use {
111 super::*,
112 crate::syscalls::tests::test_helpers::{costs, make_memory, meter},
113 sbpf_vm::{errors::SbpfVmError, memory::Memory},
114 };
115
116 #[test]
117 fn test_memcpy_basic() {
118 let mut memory = make_memory();
119 let src = Memory::HEAP_START;
120 let dst = Memory::HEAP_START + 64;
121 memory.write_bytes(src, &[1, 2, 3, 4, 5]).unwrap();
122
123 let registers = [dst, src, 5, 0, 0];
124 sol_memcpy(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
125
126 assert_eq!(memory.read_bytes(dst, 5).unwrap(), &[1, 2, 3, 4, 5]);
127 }
128
129 #[test]
130 fn test_memcpy_overlapping_returns_error() {
131 let mut memory = make_memory();
132 let src = Memory::HEAP_START;
134 let dst = Memory::HEAP_START + 4;
135 memory.write_bytes(src, &[0u8; 16]).unwrap();
136
137 let registers = [dst, src, 8, 0, 0];
138 assert!(matches!(
139 sol_memcpy(registers, &mut memory, &meter(1_000_000), &costs()),
140 Err(SbpfVmError::OverlappingMemoryRegions)
141 ));
142 }
143
144 #[test]
145 fn test_memcpy_adjacent_not_overlapping() {
146 let mut memory = make_memory();
147 let src = Memory::HEAP_START;
148 let dst = Memory::HEAP_START + 5;
149 memory.write_bytes(src, &[10, 20, 30, 40, 50]).unwrap();
150
151 let registers = [dst, src, 5, 0, 0];
152 sol_memcpy(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
153 assert_eq!(memory.read_bytes(dst, 5).unwrap(), &[10, 20, 30, 40, 50]);
154 }
155
156 #[test]
157 fn test_memcpy_zero_length() {
158 let mut memory = make_memory();
159 let src = Memory::HEAP_START;
160 let dst = Memory::HEAP_START + 32;
161 memory.write_bytes(src, &[0xAA]).unwrap();
162
163 let registers = [dst, src, 0, 0, 0];
164 sol_memcpy(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
165 assert_eq!(memory.read_u8(dst).unwrap(), 0);
166 }
167
168 #[test]
169 fn test_memcpy_compute_exhausted() {
170 let mut memory = make_memory();
171 memory
172 .write_bytes(Memory::HEAP_START, &[1, 2, 3, 4, 5])
173 .unwrap();
174
175 let registers = [Memory::HEAP_START + 64, Memory::HEAP_START, 5, 0, 0];
176 assert!(matches!(
178 sol_memcpy(registers, &mut memory, &meter(9), &costs()),
179 Err(SbpfVmError::ComputeBudgetExceeded { .. })
180 ));
181 }
182
183 #[test]
184 fn test_memcpy_oob_dst() {
185 let mut memory = make_memory();
186 let src = Memory::HEAP_START;
187 let heap_size = 64 * 1024u64;
188 let dst = Memory::HEAP_START + heap_size - 3;
189 memory.write_bytes(src, &[1, 2, 3, 4, 5]).unwrap();
190
191 let registers = [dst, src, 5, 0, 0];
192 let result = sol_memcpy(registers, &mut memory, &meter(1_000_000), &costs());
193 assert!(result.is_err());
194 }
195
196 #[test]
197 fn test_memcpy_oob_src() {
198 let mut memory = make_memory();
199 let heap_size = 64 * 1024u64;
200 let src = Memory::HEAP_START + heap_size - 2;
201 let dst = Memory::HEAP_START;
202
203 let registers = [dst, src, 5, 0, 0];
204 let result = sol_memcpy(registers, &mut memory, &meter(1_000_000), &costs());
205 assert!(result.is_err());
206 }
207
208 #[test]
209 fn test_memmove_basic() {
210 let mut memory = make_memory();
211 let src = Memory::HEAP_START;
212 let dst = Memory::HEAP_START + 64;
213 memory.write_bytes(src, &[5, 4, 3, 2, 1]).unwrap();
214
215 let registers = [dst, src, 5, 0, 0];
216 sol_memmove(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
217 assert_eq!(memory.read_bytes(dst, 5).unwrap(), &[5, 4, 3, 2, 1]);
218 }
219
220 #[test]
221 fn test_memmove_overlapping_allowed() {
222 let mut memory = make_memory();
223 let src = Memory::HEAP_START;
224 let dst = Memory::HEAP_START + 4;
225 memory.write_bytes(src, &[1, 2, 3, 4, 5, 6, 7, 8]).unwrap();
226
227 let registers = [dst, src, 8, 0, 0];
228 sol_memmove(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
229 }
230
231 #[test]
232 fn test_memmove_compute_exhausted() {
233 let mut memory = make_memory();
234 memory.write_bytes(Memory::HEAP_START, &[1]).unwrap();
235 let registers = [Memory::HEAP_START + 64, Memory::HEAP_START, 1, 0, 0];
236 assert!(matches!(
237 sol_memmove(registers, &mut memory, &meter(9), &costs()),
238 Err(SbpfVmError::ComputeBudgetExceeded { .. })
239 ));
240 }
241
242 #[test]
243 fn test_memset_basic() {
244 let mut memory = make_memory();
245 let dst = Memory::HEAP_START;
246
247 let registers = [dst, 0xAB, 8, 0, 0];
248 sol_memset(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
249
250 assert_eq!(memory.read_bytes(dst, 8).unwrap(), &[0xAB; 8]);
251 }
252
253 #[test]
254 fn test_memset_zero_fill() {
255 let mut memory = make_memory();
256 let dst = Memory::HEAP_START;
257 memory.write_bytes(dst, &[0xFF; 4]).unwrap();
258
259 let registers = [dst, 0x00, 4, 0, 0];
260 sol_memset(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
261 assert_eq!(memory.read_bytes(dst, 4).unwrap(), &[0u8; 4]);
262 }
263
264 #[test]
265 fn test_memset_compute_exhausted() {
266 let mut memory = make_memory();
267 let registers = [Memory::HEAP_START, 0xFF, 5, 0, 0];
268 assert!(matches!(
269 sol_memset(registers, &mut memory, &meter(9), &costs()),
270 Err(SbpfVmError::ComputeBudgetExceeded { .. })
271 ));
272 }
273
274 #[test]
275 fn test_memcmp_equal() {
276 let mut memory = make_memory();
277 let s1 = Memory::HEAP_START;
278 let s2 = Memory::HEAP_START + 16;
279 let result_ptr = Memory::HEAP_START + 32;
280 memory.write_bytes(s1, &[1, 2, 3, 4, 5]).unwrap();
281 memory.write_bytes(s2, &[1, 2, 3, 4, 5]).unwrap();
282
283 let registers = [s1, s2, 5, result_ptr, 0];
284 sol_memcmp(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
285
286 let cmp = memory.read_u32(result_ptr).unwrap() as i32;
287 assert_eq!(cmp, 0);
288 }
289
290 #[test]
291 fn test_memcmp_less_than() {
292 let mut memory = make_memory();
293 let s1 = Memory::HEAP_START;
294 let s2 = Memory::HEAP_START + 16;
295 let result_ptr = Memory::HEAP_START + 32;
296 memory.write_bytes(s1, &[1, 2, 3]).unwrap();
298 memory.write_bytes(s2, &[1, 2, 4]).unwrap();
299
300 let registers = [s1, s2, 3, result_ptr, 0];
301 sol_memcmp(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
302
303 let cmp = memory.read_u32(result_ptr).unwrap() as i32;
304 assert!(cmp < 0);
305 }
306
307 #[test]
308 fn test_memcmp_greater_than() {
309 let mut memory = make_memory();
310 let s1 = Memory::HEAP_START;
311 let s2 = Memory::HEAP_START + 16;
312 let result_ptr = Memory::HEAP_START + 32;
313 memory.write_bytes(s1, &[1, 2, 5]).unwrap();
314 memory.write_bytes(s2, &[1, 2, 4]).unwrap();
315
316 let registers = [s1, s2, 3, result_ptr, 0];
317 sol_memcmp(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
318
319 let cmp = memory.read_u32(result_ptr).unwrap() as i32;
320 assert!(cmp > 0);
321 }
322
323 #[test]
324 fn test_memcmp_compute_exhausted() {
325 let mut memory = make_memory();
326 let s1 = Memory::HEAP_START;
327 let s2 = Memory::HEAP_START + 16;
328 let result_ptr = Memory::HEAP_START + 32;
329 let registers = [s1, s2, 5, result_ptr, 0];
330 assert!(matches!(
331 sol_memcmp(registers, &mut memory, &meter(9), &costs()),
332 Err(SbpfVmError::ComputeBudgetExceeded { .. })
333 ));
334 }
335}