1use {super::*, crate::translate_mut};
2
3fn mem_op_consume(invoke_context: &mut InvokeContext, n: u64) -> Result<(), Error> {
4 let compute_cost = invoke_context.get_execution_cost();
5 let cost = compute_cost.mem_op_base_cost.max(
6 n.checked_div(compute_cost.cpi_bytes_per_unit)
7 .unwrap_or(u64::MAX),
8 );
9 invoke_context.compute_meter.consume_checked(cost)
10}
11
12pub(crate) fn is_nonoverlapping<N>(src: N, src_len: N, dst: N, dst_len: N) -> bool
14where
15 N: Ord + num_traits::SaturatingSub,
16{
17 if src > dst {
20 src.saturating_sub(&dst) >= dst_len
21 } else {
22 dst.saturating_sub(&src) >= src_len
23 }
24}
25
26declare_builtin_function!(
27 SyscallMemcpy,
29 fn rust(
30 invoke_context: &mut InvokeContext<'_, '_>,
31 dst_addr: u64,
32 src_addr: u64,
33 n: u64,
34 _arg4: u64,
35 _arg5: u64,
36 ) -> Result<u64, Error> {
37 mem_op_consume(invoke_context, n)?;
38
39 if !is_nonoverlapping(src_addr, n, dst_addr, n) {
40 return Err(SyscallError::CopyOverlapping.into());
41 }
42
43 memmove(invoke_context, dst_addr, src_addr, n)
45 }
46);
47
48declare_builtin_function!(
49 SyscallMemmove,
51 fn rust(
52 invoke_context: &mut InvokeContext<'_, '_>,
53 dst_addr: u64,
54 src_addr: u64,
55 n: u64,
56 _arg4: u64,
57 _arg5: u64,
58 ) -> Result<u64, Error> {
59 mem_op_consume(invoke_context, n)?;
60 memmove(invoke_context, dst_addr, src_addr, n)
61 }
62);
63
64declare_builtin_function!(
65 SyscallMemcmp,
67 fn rust(
68 invoke_context: &mut InvokeContext<'_, '_>,
69 s1_addr: u64,
70 s2_addr: u64,
71 n: u64,
72 cmp_result_addr: u64,
73 _arg5: u64,
74 ) -> Result<u64, Error> {
75 mem_op_consume(invoke_context, n)?;
76 let check_aligned = invoke_context.get_check_aligned();
77 let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
78
79 let s1 = translate_slice::<u8>(
80 memory_mapping,
81 s1_addr,
82 n,
83 check_aligned,
84 )?;
85 let s2 = translate_slice::<u8>(
86 memory_mapping,
87 s2_addr,
88 n,
89 check_aligned,
90 )?;
91
92 debug_assert_eq!(s1.len(), n as usize);
93 debug_assert_eq!(s2.len(), n as usize);
94 let result = unsafe { memcmp(s1, s2, n as usize) };
99
100 translate_mut!(
101 memory_mapping,
102 check_aligned,
103 let cmp_result_ref_mut: (&mut std::mem::MaybeUninit<i32>) = map(cmp_result_addr)?;
104 );
105 cmp_result_ref_mut.write(result);
106
107 Ok(0)
108 }
109);
110
111declare_builtin_function!(
112 SyscallMemset,
114 fn rust(
115 invoke_context: &mut InvokeContext<'_, '_>,
116 dst_addr: u64,
117 c: u64,
118 n: u64,
119 _arg4: u64,
120 _arg5: u64,
121 ) -> Result<u64, Error> {
122 mem_op_consume(invoke_context, n)?;
123
124 let check_aligned = invoke_context.get_check_aligned();
125 let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
126 translate_mut!(
127 memory_mapping,
128 check_aligned,
129 let s: (&mut [MaybeUninit<u8>]) = map(dst_addr, n)?;
130 );
131 s.fill(MaybeUninit::new(c as u8));
132 Ok(0)
133 }
134);
135
136fn memmove(
137 invoke_context: &mut InvokeContext,
138 dst_addr: u64,
139 src_addr: u64,
140 n: u64,
141) -> Result<u64, Error> {
142 let check_aligned = invoke_context.get_check_aligned();
143 let memory_mapping = invoke_context.memory_contexts.memory_mapping_mut()?;
144 touch_slice_mut::<MaybeUninit<u8>>(memory_mapping, dst_addr, n)?;
147 let slice = translate_slice_inner!(
148 memory_mapping,
149 AccessType::Store,
150 dst_addr,
151 n,
152 MaybeUninit<u8>,
153 check_aligned,
154 )?;
155 let src_ptr = translate_slice::<u8>(memory_mapping, src_addr, n, check_aligned)?.as_ptr();
156 unsafe { std::ptr::copy(src_ptr.cast(), slice as *mut MaybeUninit<u8>, n as usize) };
157 Ok(0)
158}
159
160unsafe fn memcmp(s1: &[u8], s2: &[u8], n: usize) -> i32 {
162 let (s1pre, s1mid, s1end) = unsafe {
163 s1.get_unchecked(..n).align_to::<u128>()
165 };
166 let mut s2ptr = s2.as_ptr();
167 for s1pre_byte in s1pre.iter().copied() {
168 unsafe {
169 let s2pre_byte = *s2ptr;
172 if s1pre_byte != s2pre_byte {
173 return i32::from(s1pre_byte).wrapping_sub(s2pre_byte.into());
174 }
175 s2ptr = s2ptr.add(1);
176 }
177 }
178 for s1mid_value in s1mid.iter().copied() {
179 let s2mid_value = unsafe {
180 s2ptr.cast::<u128>().read_unaligned().to_le()
184 };
185 if s1mid_value != s2mid_value {
186 let (s1_word, s2_word) = if s1mid_value as u64 != s2mid_value as u64 {
191 let w1 = s1mid_value as u64;
192 let w2 = s2mid_value as u64;
193 (w1, w2)
194 } else {
195 let w1 = (s1mid_value >> 64) as u64;
196 let w2 = (s2mid_value >> 64) as u64;
197 (w1, w2)
198 };
199 let shift = (s1_word ^ s2_word).trailing_zeros() & !7;
200 let b1 = (s1_word >> shift) as u8;
201 let b2 = (s2_word >> shift) as u8;
202 return i32::from(b1).wrapping_sub(b2.into());
203 }
204 unsafe {
205 s2ptr = s2ptr.add(std::mem::size_of::<u128>());
208 }
209 }
210 for s1end_byte in s1end.iter().copied() {
211 unsafe {
212 let s2end_byte = *s2ptr;
214 if s1end_byte != s2end_byte {
215 return i32::from(s1end_byte).wrapping_sub(s2end_byte.into());
216 }
217 s2ptr = s2ptr.add(1);
218 }
219 }
220 0
221}
222
223#[cfg(test)]
224#[allow(clippy::indexing_slicing)]
225#[allow(clippy::arithmetic_side_effects)]
226mod tests {
227 use super::*;
228
229 #[test]
230 fn test_is_nonoverlapping() {
231 for dst in 0..8 {
232 assert!(is_nonoverlapping(10, 3, dst, 3));
233 }
234 for dst in 8..13 {
235 assert!(!is_nonoverlapping(10, 3, dst, 3));
236 }
237 for dst in 13..20 {
238 assert!(is_nonoverlapping(10, 3, dst, 3));
239 }
240 assert!(is_nonoverlapping::<u8>(255, 3, 254, 1));
241 assert!(!is_nonoverlapping::<u8>(255, 2, 254, 3));
242 }
243}