Skip to content

Commit f94c831

Browse files
feat: implemented prevent_segment_execution function
Signed-off-by: Dori Medini <[email protected]>
1 parent fc100d9 commit f94c831

File tree

4 files changed

+127
-0
lines changed

4 files changed

+127
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#### Upcoming Changes
44

5+
* feat: implemented prevent_contract_segment_execution function [#2099](https://github.com/lambdaclass/cairo-vm/pull/2099)
6+
57
#### [2.1.0] - 2025-05-21
68

79
* chore: bump pip `cairo-lang` 0.13.5 [#1959](https://github.com/lambdaclass/cairo-vm/pull/1959)

vm/src/vm/errors/memory_errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ use crate::types::{
1515

1616
#[derive(Debug, PartialEq, Error)]
1717
pub enum MemoryError {
18+
#[error("Cell {0} has already been accessed; it cannot be execution-protected.")]
19+
CellAlreadyAccessed(Relocatable),
1820
#[error(transparent)]
1921
Math(#[from] MathError),
2022
#[error(transparent)]

vm/src/vm/vm_core.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,17 @@ impl VirtualMachine {
946946
self.segments.memory.insert_value(key, val)
947947
}
948948

949+
/// Unset (delete) the first cell of the given contract segment, to prevent contract segment
950+
/// execution. Returns an error if the first cell of the contract segment is already accessed.
951+
pub fn prevent_contract_segment_execution(
952+
&mut self,
953+
contract_segment_start: Relocatable,
954+
) -> Result<(), MemoryError> {
955+
self.segments
956+
.memory
957+
.prevent_contract_segment_execution(contract_segment_start)
958+
}
959+
949960
///Writes data into the memory from address ptr and returns the first address after the data.
950961
pub fn load_data(
951962
&mut self,
@@ -4727,6 +4738,80 @@ mod tests {
47274738
assert_eq!(vm.segments.compute_effective_sizes(), &vec![4]);
47284739
}
47294740

4741+
#[test]
4742+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
4743+
fn test_prevent_contract_segment_execution() {
4744+
let mut vm = vm!();
4745+
4746+
let segment0 = vm.segments.add();
4747+
let segment1 = vm.segments.add();
4748+
let segment2 = vm.segments.add();
4749+
let segment3 = vm.segments.add();
4750+
let tmp_segment = vm.add_temporary_segment();
4751+
assert_eq!(segment0.segment_index, 0);
4752+
assert_eq!(segment1.segment_index, 1);
4753+
assert_eq!(segment2.segment_index, 2);
4754+
assert_eq!(segment3.segment_index, 3);
4755+
assert_eq!(tmp_segment.segment_index, -1);
4756+
vm.segments.memory = memory![
4757+
((0, 1), 1),
4758+
((1, 0), 3),
4759+
((1, 1), 4),
4760+
((2, 0), 7),
4761+
((3, 0), 7),
4762+
((-1, 0), 5),
4763+
((-1, 1), 5),
4764+
((-1, 2), 5)
4765+
];
4766+
vm.run_finished = true;
4767+
4768+
vm.mark_address_range_as_accessed((2, 0).into(), 1).unwrap();
4769+
4770+
let cell0 = Relocatable::from((0, 0));
4771+
let cell1 = Relocatable::from((1, 1));
4772+
let cell2 = Relocatable::from((2, 0));
4773+
let cell3 = Relocatable::from((3, 7));
4774+
let cell7 = Relocatable::from((7, 17));
4775+
let cell_tmp = Relocatable::from((-1, 1));
4776+
vm.prevent_contract_segment_execution(cell0).unwrap();
4777+
vm.prevent_contract_segment_execution(cell1).unwrap();
4778+
vm.prevent_contract_segment_execution(cell_tmp).unwrap();
4779+
vm.prevent_contract_segment_execution(cell3).unwrap();
4780+
4781+
// Check that the cells were set to NONE.
4782+
assert!(vm
4783+
.segments
4784+
.memory
4785+
.get_cell_for_testing(cell0)
4786+
.unwrap()
4787+
.is_none());
4788+
assert!(vm
4789+
.segments
4790+
.memory
4791+
.get_cell_for_testing(cell1)
4792+
.unwrap()
4793+
.is_none());
4794+
assert!(vm
4795+
.segments
4796+
.memory
4797+
.get_cell_for_testing(cell_tmp)
4798+
.unwrap()
4799+
.is_none());
4800+
// Segment 3 cell was out of offset range, so it should not be modified or allocated.
4801+
assert!(vm.segments.memory.get_cell_for_testing(cell3).is_none());
4802+
// Segment 2 cell was accessed, so attempting to unset the memory should result in error.
4803+
assert_matches!(
4804+
vm.prevent_contract_segment_execution(cell2).unwrap_err(),
4805+
MemoryError::CellAlreadyAccessed(relocatable) if relocatable == cell2
4806+
);
4807+
// Segment 7 was not allocated, so attempting to unset the memory should result in error.
4808+
assert_matches!(
4809+
vm.prevent_contract_segment_execution(cell7).unwrap_err(),
4810+
MemoryError::UnallocatedSegment(boxed)
4811+
if *boxed == (cell7.segment_index.try_into().unwrap(), cell7.offset)
4812+
);
4813+
}
4814+
47304815
#[test]
47314816
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
47324817
fn mark_as_accessed() {

vm/src/vm/vm_memory/memory.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,39 @@ impl Memory {
237237
self.validate_memory_cell(key)
238238
}
239239

240+
/// Unset (delete) the first cell of the given contract segment, to prevent contract segment
241+
/// execution. Returns an error if the first cell of the contract segment is already accessed.
242+
pub fn prevent_contract_segment_execution(
243+
&mut self,
244+
contract_segment_start: Relocatable,
245+
) -> Result<(), MemoryError> {
246+
let data = if contract_segment_start.segment_index.is_negative() {
247+
&mut self.temp_data
248+
} else {
249+
&mut self.data
250+
};
251+
let (index, offset) = from_relocatable_to_indexes(contract_segment_start);
252+
253+
let data_len = data.len();
254+
let segment = data
255+
.get_mut(index)
256+
.ok_or_else(|| MemoryError::UnallocatedSegment(Box::new((index, data_len))))?;
257+
258+
// Check if the offset exists; if not, there is nothing to unset.
259+
if offset >= segment.len() {
260+
return Ok(());
261+
}
262+
263+
// Ensure the cell has not been accessed.
264+
if segment[offset].is_accessed() {
265+
return Err(MemoryError::CellAlreadyAccessed(contract_segment_start));
266+
}
267+
268+
// Unset the cell.
269+
segment[offset] = MemoryCell::NONE;
270+
Ok(())
271+
}
272+
240273
/// Retrieve a value from memory (either normal or temporary) and apply relocation rules
241274
pub(crate) fn get<'a, 'b: 'a, K: 'a>(&'b self, key: &'a K) -> Option<Cow<'b, MaybeRelocatable>>
242275
where
@@ -671,6 +704,11 @@ impl Memory {
671704
data.get(i)?.get(j)
672705
}
673706

707+
#[cfg(test)]
708+
pub(crate) fn get_cell_for_testing(&self, addr: Relocatable) -> Option<&MemoryCell> {
709+
self.get_cell(addr)
710+
}
711+
674712
pub fn is_accessed(&self, addr: &Relocatable) -> Result<bool, MemoryError> {
675713
Ok(self
676714
.get_cell(*addr)

0 commit comments

Comments
 (0)