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