use crate::stdlib::prelude::*;
use num_traits::ToPrimitive;
use super::{
errors::{runner_errors::RunnerError, vm_errors::VirtualMachineError},
runners::cairo_runner::CairoRunner,
};
use crate::types::relocatable::MaybeRelocatable;
pub fn verify_secure_runner(
runner: &CairoRunner,
verify_builtins: bool,
program_segment_size: Option<usize>,
) -> Result<(), VirtualMachineError> {
let builtins_segment_info = match verify_builtins {
true => runner.get_builtin_segments_info()?,
false => Vec::new(),
};
for (index, stop_ptr) in builtins_segment_info {
let current_size = runner
.vm
.segments
.memory
.data
.get(index)
.map(|segment| segment.len());
if current_size >= Some(stop_ptr + 1) {
return Err(VirtualMachineError::OutOfBoundsBuiltinSegmentAccess);
}
}
let program_segment_index = runner
.program_base
.and_then(|rel| rel.segment_index.to_usize())
.ok_or(RunnerError::NoProgBase)?;
let program_segment_size =
program_segment_size.unwrap_or(runner.program.shared_program_data.data.len());
let program_length = runner
.vm
.segments
.memory
.data
.get(program_segment_index)
.map(|segment| segment.len());
if program_length >= Some(program_segment_size + 1) {
return Err(VirtualMachineError::OutOfBoundsProgramSegmentAccess);
}
if !runner.vm.segments.memory.temp_data.is_empty() {
for value in runner.vm.segments.memory.data.iter().flatten() {
match value.get_value() {
Some(MaybeRelocatable::RelocatableValue(addr)) if addr.segment_index < 0 => {
return Err(VirtualMachineError::InvalidMemoryValueTemporaryAddress(
Box::new(addr),
))
}
_ => {}
}
}
}
for builtin in runner.vm.builtin_runners.iter() {
builtin.run_security_checks(&runner.vm)?;
}
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor;
use crate::types::builtin_name::BuiltinName;
use crate::types::relocatable::Relocatable;
use crate::Felt252;
use crate::{relocatable, types::program::Program, utils::test_utils::*};
use assert_matches::assert_matches;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn verify_secure_runner_without_program_base() {
let program = program!();
let runner = cairo_runner!(program);
assert_matches!(
verify_secure_runner(&runner, true, None),
Err(VirtualMachineError::RunnerError(RunnerError::NoProgBase))
);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn verify_secure_runner_empty_memory() {
let program = program!(main = Some(0),);
let mut runner = cairo_runner!(program);
runner.initialize(false).unwrap();
runner.vm.segments.compute_effective_sizes();
assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn verify_secure_runner_program_access_out_of_bounds() {
let program = program!(main = Some(0),);
let mut runner = cairo_runner!(program);
runner.initialize(false).unwrap();
runner.vm.segments = segments![((0, 0), 100)];
runner.vm.segments.segment_used_sizes = Some(vec![1]);
assert_matches!(
verify_secure_runner(&runner, true, None),
Err(VirtualMachineError::OutOfBoundsProgramSegmentAccess)
);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn verify_secure_runner_program_with_program_size() {
let program = program!(main = Some(0),);
let mut runner = cairo_runner!(program);
runner.initialize(false).unwrap();
runner.vm.segments = segments![((0, 0), 100)];
runner.vm.segments.segment_used_sizes = Some(vec![1]);
assert_matches!(verify_secure_runner(&runner, true, Some(1)), Ok(()));
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn verify_secure_runner_builtin_access_out_of_bounds() {
let program = program!(main = Some(0), builtins = vec![BuiltinName::range_check],);
let mut runner = cairo_runner!(program);
runner.initialize(false).unwrap();
runner.vm.builtin_runners[0].set_stop_ptr(0);
runner.vm.segments.memory = memory![((2, 0), 1)];
runner.vm.segments.segment_used_sizes = Some(vec![0, 0, 0, 0]);
assert_matches!(
verify_secure_runner(&runner, true, None),
Err(VirtualMachineError::OutOfBoundsBuiltinSegmentAccess)
);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn verify_secure_runner_builtin_access_correct() {
let program = program!(main = Some(0), builtins = vec![BuiltinName::range_check],);
let mut runner = cairo_runner!(program);
runner.initialize(false).unwrap();
let mut hint_processor = BuiltinHintProcessor::new_empty();
runner.end_run(false, false, &mut hint_processor).unwrap();
runner.vm.builtin_runners[0].set_stop_ptr(1);
runner.vm.segments.memory = memory![((2, 0), 1)];
runner.vm.segments.segment_used_sizes = Some(vec![0, 0, 1, 0]);
assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn verify_secure_runner_success() {
let program = program!(
data = vec![
Felt252::ZERO.into(),
Felt252::ZERO.into(),
Felt252::ZERO.into(),
Felt252::ZERO.into(),
],
main = Some(0),
);
let mut runner = cairo_runner!(program);
runner.initialize(false).unwrap();
runner.vm.segments.memory = memory![
((0, 0), (1, 0)),
((0, 1), (2, 1)),
((0, 2), (3, 2)),
((0, 3), (4, 3))
];
runner.vm.segments.segment_used_sizes = Some(vec![5, 1, 2, 3, 4]);
assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn verify_secure_runner_temporary_memory_properly_relocated() {
let program = program!(
data = vec![
Felt252::ZERO.into(),
Felt252::ZERO.into(),
Felt252::ZERO.into(),
Felt252::ZERO.into(),
],
main = Some(0),
);
let mut runner = cairo_runner!(program);
runner.initialize(false).unwrap();
runner.vm.segments.memory = memory![
((0, 1), (1, 0)),
((0, 2), (2, 1)),
((0, 3), (3, 2)),
((-1, 0), (1, 2))
];
runner.vm.segments.segment_used_sizes = Some(vec![5, 1, 2, 3, 4]);
assert_matches!(verify_secure_runner(&runner, true, None), Ok(()));
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn verify_secure_runner_temporary_memory_not_fully_relocated() {
let program = program!(
data = vec![
Felt252::ZERO.into(),
Felt252::ZERO.into(),
Felt252::ZERO.into(),
Felt252::ZERO.into(),
],
main = Some(0),
);
let mut runner = cairo_runner!(program);
runner.initialize(false).unwrap();
runner.vm.segments.memory = memory![
((0, 0), (1, 0)),
((0, 1), (2, 1)),
((0, 2), (-3, 2)),
((0, 3), (4, 3)),
((-1, 0), (1, 2))
];
runner.vm.segments.segment_used_sizes = Some(vec![5, 1, 2, 3, 4]);
assert_matches!(
verify_secure_runner(&runner, true, None),
Err(VirtualMachineError::InvalidMemoryValueTemporaryAddress(
bx
)) if *bx == relocatable!(-3, 2)
);
}
}