1use crate::stdlib::prelude::*;
2
3use num_traits::ToPrimitive;
4
5use super::{
6 errors::{runner_errors::RunnerError, vm_errors::VirtualMachineError},
7 runners::cairo_runner::{CairoRunner, RunnerMode},
8};
9use crate::types::relocatable::MaybeRelocatable;
10use crate::Felt252;
11
12pub fn verify_secure_runner(
24 runner: &CairoRunner,
25 verify_builtins: bool,
26 program_segment_size: Option<usize>,
27) -> Result<(), VirtualMachineError> {
28 let builtins_segment_info = match verify_builtins {
29 true => runner.get_builtin_segments_info()?,
30 false => Vec::new(),
31 };
32 for (index, stop_ptr) in builtins_segment_info {
34 let current_size = runner
35 .vm
36 .segments
37 .memory
38 .data
39 .get(index)
40 .map(|segment| segment.len());
41 if current_size >= Some(stop_ptr + 1) {
43 return Err(VirtualMachineError::OutOfBoundsBuiltinSegmentAccess);
44 }
45 }
46 let program_segment_index = runner
48 .program_base
49 .and_then(|rel| rel.segment_index.to_usize())
50 .ok_or(RunnerError::NoProgBase)?;
51 let program_segment_size =
52 program_segment_size.unwrap_or(runner.program.shared_program_data.data.len());
53 let program_length = runner
54 .vm
55 .segments
56 .memory
57 .data
58 .get(program_segment_index)
59 .map(|segment| segment.len());
60 if program_length >= Some(program_segment_size + 1) {
62 return Err(VirtualMachineError::OutOfBoundsProgramSegmentAccess);
63 }
64 if !runner.vm.segments.memory.temp_data.is_empty() {
68 for value in runner.vm.segments.memory.data.iter().flatten() {
69 match value.get_value() {
70 Some(MaybeRelocatable::RelocatableValue(addr)) if addr.segment_index < 0 => {
71 return Err(VirtualMachineError::InvalidMemoryValueTemporaryAddress(
72 Box::new(addr),
73 ))
74 }
75 _ => {}
76 }
77 }
78 }
79 for builtin in runner.vm.builtin_runners.iter() {
80 builtin.run_security_checks(&runner.vm)?;
81 }
82
83 let initial_fp = runner
85 .get_initial_fp()
86 .ok_or(VirtualMachineError::MissingInitialFp)?;
87 let ret_fp_addr = (initial_fp - 2).map_err(VirtualMachineError::Math)?;
88 let ret_fp = runner
89 .vm
90 .get_maybe(&ret_fp_addr)
91 .ok_or(VirtualMachineError::MissingReturnFp(Box::new(ret_fp_addr)))?;
92 let final_fp = runner.vm.get_fp();
93 match ret_fp {
94 MaybeRelocatable::RelocatableValue(value) => {
95 if runner.runner_mode == RunnerMode::ProofModeCanonical && value != final_fp {
96 return Err(VirtualMachineError::MismatchReturnFP(Box::new((
97 value, final_fp,
98 ))));
99 }
100 if runner.runner_mode == RunnerMode::ExecutionMode && value.offset != final_fp.offset {
101 return Err(VirtualMachineError::MismatchReturnFPOffset(Box::new((
102 value, final_fp,
103 ))));
104 }
105 }
106 MaybeRelocatable::Int(value) => {
107 if Felt252::from(final_fp.offset) != value {
108 return Err(VirtualMachineError::MismatchReturnFPFelt(Box::new((
109 value, final_fp,
110 ))));
111 }
112 }
113 }
114 Ok(())
115}
116
117#[cfg(test)]
118mod test {
119 use super::*;
120 use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor;
121
122 use crate::types::builtin_name::BuiltinName;
123 use crate::types::relocatable::Relocatable;
124
125 use crate::Felt252;
126 use crate::{relocatable, types::program::Program, utils::test_utils::*};
127 use assert_matches::assert_matches;
128
129 #[cfg(target_arch = "wasm32")]
130 use wasm_bindgen_test::*;
131
132 #[test]
133 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
134 fn verify_secure_runner_without_program_base() {
135 let program = program!();
136
137 let runner = cairo_runner!(program);
138
139 assert_matches!(
140 verify_secure_runner(&runner, true, None),
141 Err(VirtualMachineError::RunnerError(RunnerError::NoProgBase))
142 );
143 }
144
145 #[test]
146 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
147 fn verify_secure_runner_empty_memory() {
148 let program = program!(main = Some(0),);
149 let mut runner = cairo_runner!(program);
150 runner.initialize(false).unwrap();
151 let mut hint_processor = BuiltinHintProcessor::new_empty();
153 runner.end_run(false, false, &mut hint_processor).unwrap();
154 runner.vm.run_context.fp = 0;
157 assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
158 }
159
160 #[test]
161 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
162 fn verify_secure_runner_program_access_out_of_bounds() {
163 let program = program!(main = Some(0),);
164 let mut runner = cairo_runner!(program);
165
166 runner.initialize(false).unwrap();
167
168 runner.vm.segments = segments![((0, 0), 100)];
169 runner.vm.segments.segment_used_sizes = Some(vec![1]);
170
171 assert_matches!(
172 verify_secure_runner(&runner, true, None),
173 Err(VirtualMachineError::OutOfBoundsProgramSegmentAccess)
174 );
175 }
176
177 #[test]
178 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
179 fn verify_secure_runner_program_with_program_size() {
180 let program = program!(main = Some(0),);
181 let mut runner = cairo_runner!(program);
182
183 runner.initialize(false).unwrap();
184 runner.vm.segments = segments![((0, 0), 100), ((1, 0), 0)];
186 runner.vm.segments.segment_used_sizes = Some(vec![1]);
187 runner.vm.run_context.fp = 0;
190 assert_matches!(verify_secure_runner(&runner, true, Some(1)), Ok(()));
191 }
192
193 #[test]
194 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
195 fn verify_secure_runner_builtin_access_out_of_bounds() {
196 let program = program!(main = Some(0), builtins = vec![BuiltinName::range_check],);
197 let mut runner = cairo_runner!(program);
198
199 runner.initialize(false).unwrap();
200 runner.vm.builtin_runners[0].set_stop_ptr(0);
201 runner.vm.segments.memory = memory![((2, 0), 1)];
202 runner.vm.segments.segment_used_sizes = Some(vec![0, 0, 0, 0]);
203
204 assert_matches!(
205 verify_secure_runner(&runner, true, None),
206 Err(VirtualMachineError::OutOfBoundsBuiltinSegmentAccess)
207 );
208 }
209
210 #[test]
211 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
212 fn verify_secure_runner_builtin_access_correct() {
213 let program = program!(main = Some(0), builtins = vec![BuiltinName::range_check],);
214 let mut runner = cairo_runner!(program);
215
216 runner.initialize(false).unwrap();
217 let mut hint_processor = BuiltinHintProcessor::new_empty();
218 runner.end_run(false, false, &mut hint_processor).unwrap();
219 runner.vm.builtin_runners[0].set_stop_ptr(1);
220 runner.vm.segments.memory = memory![((2, 0), 1), ((1, 1), (3, 0))];
222 runner.vm.run_context.fp = 0;
225 runner.vm.segments.segment_used_sizes = Some(vec![0, 0, 1, 0]);
226
227 assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
228 }
229
230 #[test]
231 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
232 fn verify_secure_runner_success() {
233 let program = program!(
234 data = vec![
235 Felt252::ZERO.into(),
236 Felt252::ZERO.into(),
237 Felt252::ZERO.into(),
238 Felt252::ZERO.into(),
239 ],
240 main = Some(0),
241 );
242
243 let mut runner = cairo_runner!(program);
244
245 runner.initialize(false).unwrap();
246 runner.vm.segments.memory = memory![
248 ((0, 0), (1, 0)),
249 ((0, 1), (2, 1)),
250 ((0, 2), (3, 2)),
251 ((0, 3), (4, 3)),
252 ((1, 0), 0)
253 ];
254 runner.vm.segments.segment_used_sizes = Some(vec![5, 1, 2, 3, 4]);
255 runner.vm.run_context.fp = 0;
258
259 assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
260 }
261
262 #[test]
263 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
264 fn verify_secure_runner_temporary_memory_properly_relocated() {
265 let program = program!(
266 data = vec![
267 Felt252::ZERO.into(),
268 Felt252::ZERO.into(),
269 Felt252::ZERO.into(),
270 Felt252::ZERO.into(),
271 ],
272 main = Some(0),
273 );
274
275 let mut runner = cairo_runner!(program);
276
277 runner.initialize(false).unwrap();
279 runner.vm.segments.memory = memory![
280 ((0, 1), (1, 0)),
281 ((0, 2), (2, 1)),
282 ((0, 3), (3, 2)),
283 ((-1, 0), (1, 2)),
284 ((1, 0), 0)
285 ];
286 runner.vm.segments.segment_used_sizes = Some(vec![5, 1, 2, 3, 4]);
287 runner.vm.run_context.fp = 0;
290
291 assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
292 }
293
294 #[test]
295 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
296 fn verify_secure_runner_temporary_memory_not_fully_relocated() {
297 let program = program!(
298 data = vec![
299 Felt252::ZERO.into(),
300 Felt252::ZERO.into(),
301 Felt252::ZERO.into(),
302 Felt252::ZERO.into(),
303 ],
304 main = Some(0),
305 );
306
307 let mut runner = cairo_runner!(program);
308
309 runner.initialize(false).unwrap();
310 runner.vm.segments.memory = memory![
312 ((0, 0), (1, 0)),
313 ((0, 1), (2, 1)),
314 ((0, 2), (-3, 2)),
315 ((0, 3), (4, 3)),
316 ((-1, 0), (1, 2)),
317 ((1, 0), 0)
318 ];
319 runner.vm.segments.segment_used_sizes = Some(vec![5, 1, 2, 3, 4]);
320
321 assert_matches!(
322 verify_secure_runner(&runner, true, None),
323 Err(VirtualMachineError::InvalidMemoryValueTemporaryAddress(
324 bx
325 )) if *bx == relocatable!(-3, 2)
326 );
327 }
328
329 #[test]
330 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
331 fn verify_secure_runner_missing_initial_fp_error() {
332 let program = program!(main = Some(0),);
333 let mut runner = cairo_runner!(program);
334 runner.program_base = Some(runner.vm.add_memory_segment());
336
337 assert_matches!(
338 verify_secure_runner(&runner, true, None),
339 Err(VirtualMachineError::MissingInitialFp)
340 );
341 }
342
343 #[test]
344 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
345 fn verify_secure_runner_ret_fp_address_not_in_memory() {
346 let program = program!(main = Some(0),);
347 let mut runner = cairo_runner!(program);
348 runner.initialize(false).unwrap();
349 runner.vm.segments.memory = crate::vm::vm_memory::memory::Memory::new();
351 assert_matches!(
352 verify_secure_runner(&runner, true, None),
353 Err(VirtualMachineError::MissingReturnFp(..))
354 );
355 }
356
357 #[test]
358 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
359 fn verify_secure_runner_return_fp_not_equal_final_fp_proof_mode() {
360 let program = program!(main = Some(0),);
361 let mut runner = cairo_runner!(program);
362 runner.initialize(false).unwrap();
363
364 runner.runner_mode = RunnerMode::ProofModeCanonical;
367
368 assert_matches!(
369 verify_secure_runner(&runner, true, None),
370 Err(VirtualMachineError::MismatchReturnFP(..))
371 );
372 }
373
374 #[test]
375 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
376 fn verify_secure_runner_return_fp_offset_not_equal_final_fp_offset_execution_mode() {
377 let program = program!(main = Some(0),);
378 let mut runner = cairo_runner!(program);
379 runner.initialize(false).unwrap();
380
381 assert_matches!(
383 verify_secure_runner(&runner, true, None),
384 Err(VirtualMachineError::MismatchReturnFPOffset(..))
385 );
386 }
387
388 #[test]
389 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
390 fn verify_secure_runner_return_fp_felt_not_equal_final_fp_offse() {
391 let program = program!(main = Some(0),);
392 let mut runner = cairo_runner!(program);
393 runner.initialize(false).unwrap();
394 runner.vm.segments.memory = memory![((1, 0), 0)];
396
397 assert_matches!(
399 verify_secure_runner(&runner, true, None),
400 Err(VirtualMachineError::MismatchReturnFPFelt(..))
401 );
402 }
403}