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
154 .end_run(false, false, &mut hint_processor, false)
155 .unwrap();
156 runner.vm.run_context.fp = 0;
159 assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
160 }
161
162 #[test]
163 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
164 fn verify_secure_runner_program_access_out_of_bounds() {
165 let program = program!(main = Some(0),);
166 let mut runner = cairo_runner!(program);
167
168 runner.initialize(false).unwrap();
169
170 runner.vm.segments = segments![((0, 0), 100)];
171 runner.vm.segments.segment_used_sizes = Some(vec![1]);
172
173 assert_matches!(
174 verify_secure_runner(&runner, true, None),
175 Err(VirtualMachineError::OutOfBoundsProgramSegmentAccess)
176 );
177 }
178
179 #[test]
180 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
181 fn verify_secure_runner_program_with_program_size() {
182 let program = program!(main = Some(0),);
183 let mut runner = cairo_runner!(program);
184
185 runner.initialize(false).unwrap();
186 runner.vm.segments = segments![((0, 0), 100), ((1, 0), 0)];
188 runner.vm.segments.segment_used_sizes = Some(vec![1]);
189 runner.vm.run_context.fp = 0;
192 assert_matches!(verify_secure_runner(&runner, true, Some(1)), Ok(()));
193 }
194
195 #[test]
196 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
197 fn verify_secure_runner_builtin_access_out_of_bounds() {
198 let program = program!(main = Some(0), builtins = vec![BuiltinName::range_check],);
199 let mut runner = cairo_runner!(program);
200
201 runner.initialize(false).unwrap();
202 runner.vm.builtin_runners[0].set_stop_ptr(0);
203 runner.vm.segments.memory = memory![((2, 0), 1)];
204 runner.vm.segments.segment_used_sizes = Some(vec![0, 0, 0, 0]);
205
206 assert_matches!(
207 verify_secure_runner(&runner, true, None),
208 Err(VirtualMachineError::OutOfBoundsBuiltinSegmentAccess)
209 );
210 }
211
212 #[test]
213 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
214 fn verify_secure_runner_builtin_access_correct() {
215 let program = program!(main = Some(0), builtins = vec![BuiltinName::range_check],);
216 let mut runner = cairo_runner!(program);
217
218 runner.initialize(false).unwrap();
219 let mut hint_processor = BuiltinHintProcessor::new_empty();
220 runner
221 .end_run(false, false, &mut hint_processor, false)
222 .unwrap();
223 runner.vm.builtin_runners[0].set_stop_ptr(1);
224 runner.vm.segments.memory = memory![((2, 0), 1), ((1, 1), (3, 0))];
226 runner.vm.run_context.fp = 0;
229 runner.vm.segments.segment_used_sizes = Some(vec![0, 0, 1, 0]);
230
231 assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
232 }
233
234 #[test]
235 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
236 fn verify_secure_runner_success() {
237 let program = program!(
238 data = vec![
239 Felt252::ZERO.into(),
240 Felt252::ZERO.into(),
241 Felt252::ZERO.into(),
242 Felt252::ZERO.into(),
243 ],
244 main = Some(0),
245 );
246
247 let mut runner = cairo_runner!(program);
248
249 runner.initialize(false).unwrap();
250 runner.vm.segments.memory = memory![
252 ((0, 0), (1, 0)),
253 ((0, 1), (2, 1)),
254 ((0, 2), (3, 2)),
255 ((0, 3), (4, 3)),
256 ((1, 0), 0)
257 ];
258 runner.vm.segments.segment_used_sizes = Some(vec![5, 1, 2, 3, 4]);
259 runner.vm.run_context.fp = 0;
262
263 assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
264 }
265
266 #[test]
267 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
268 fn verify_secure_runner_temporary_memory_properly_relocated() {
269 let program = program!(
270 data = vec![
271 Felt252::ZERO.into(),
272 Felt252::ZERO.into(),
273 Felt252::ZERO.into(),
274 Felt252::ZERO.into(),
275 ],
276 main = Some(0),
277 );
278
279 let mut runner = cairo_runner!(program);
280
281 runner.initialize(false).unwrap();
283 runner.vm.segments.memory = memory![
284 ((0, 1), (1, 0)),
285 ((0, 2), (2, 1)),
286 ((0, 3), (3, 2)),
287 ((-1, 0), (1, 2)),
288 ((1, 0), 0)
289 ];
290 runner.vm.segments.segment_used_sizes = Some(vec![5, 1, 2, 3, 4]);
291 runner.vm.run_context.fp = 0;
294
295 assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
296 }
297
298 #[test]
299 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
300 fn verify_secure_runner_temporary_memory_not_fully_relocated() {
301 let program = program!(
302 data = vec![
303 Felt252::ZERO.into(),
304 Felt252::ZERO.into(),
305 Felt252::ZERO.into(),
306 Felt252::ZERO.into(),
307 ],
308 main = Some(0),
309 );
310
311 let mut runner = cairo_runner!(program);
312
313 runner.initialize(false).unwrap();
314 runner.vm.segments.memory = memory![
316 ((0, 0), (1, 0)),
317 ((0, 1), (2, 1)),
318 ((0, 2), (-3, 2)),
319 ((0, 3), (4, 3)),
320 ((-1, 0), (1, 2)),
321 ((1, 0), 0)
322 ];
323 runner.vm.segments.segment_used_sizes = Some(vec![5, 1, 2, 3, 4]);
324
325 assert_matches!(
326 verify_secure_runner(&runner, true, None),
327 Err(VirtualMachineError::InvalidMemoryValueTemporaryAddress(
328 bx
329 )) if *bx == relocatable!(-3, 2)
330 );
331 }
332
333 #[test]
334 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
335 fn verify_secure_runner_missing_initial_fp_error() {
336 let program = program!(main = Some(0),);
337 let mut runner = cairo_runner!(program);
338 runner.program_base = Some(runner.vm.add_memory_segment());
340
341 assert_matches!(
342 verify_secure_runner(&runner, true, None),
343 Err(VirtualMachineError::MissingInitialFp)
344 );
345 }
346
347 #[test]
348 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
349 fn verify_secure_runner_ret_fp_address_not_in_memory() {
350 let program = program!(main = Some(0),);
351 let mut runner = cairo_runner!(program);
352 runner.initialize(false).unwrap();
353 runner.vm.segments.memory = crate::vm::vm_memory::memory::Memory::new();
355 assert_matches!(
356 verify_secure_runner(&runner, true, None),
357 Err(VirtualMachineError::MissingReturnFp(..))
358 );
359 }
360
361 #[test]
362 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
363 fn verify_secure_runner_return_fp_not_equal_final_fp_proof_mode() {
364 let program = program!(main = Some(0),);
365 let mut runner = cairo_runner!(program);
366 runner.initialize(false).unwrap();
367
368 runner.runner_mode = RunnerMode::ProofModeCanonical;
371
372 assert_matches!(
373 verify_secure_runner(&runner, true, None),
374 Err(VirtualMachineError::MismatchReturnFP(..))
375 );
376 }
377
378 #[test]
379 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
380 fn verify_secure_runner_return_fp_offset_not_equal_final_fp_offset_execution_mode() {
381 let program = program!(main = Some(0),);
382 let mut runner = cairo_runner!(program);
383 runner.initialize(false).unwrap();
384
385 assert_matches!(
387 verify_secure_runner(&runner, true, None),
388 Err(VirtualMachineError::MismatchReturnFPOffset(..))
389 );
390 }
391
392 #[test]
393 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
394 fn verify_secure_runner_return_fp_felt_not_equal_final_fp_offse() {
395 let program = program!(main = Some(0),);
396 let mut runner = cairo_runner!(program);
397 runner.initialize(false).unwrap();
398 runner.vm.segments.memory = memory![((1, 0), 0)];
400
401 assert_matches!(
403 verify_secure_runner(&runner, true, None),
404 Err(VirtualMachineError::MismatchReturnFPFelt(..))
405 );
406 }
407}