diff --git a/crates/test-files/fixtures/features/arrays.fe b/crates/test-files/fixtures/features/arrays.fe new file mode 100644 index 0000000000..b7b1214db6 --- /dev/null +++ b/crates/test-files/fixtures/features/arrays.fe @@ -0,0 +1,9 @@ +contract Foo: + my_array: Array + + pub fn get_from_storage(self, index: u256) -> u256: + return self.my_array[index] + + pub fn get_from_memory(index: u256) -> u256: + let my_array: Array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + return my_array[index] diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 5cf1ff7ed7..098cd5be6a 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -230,6 +230,10 @@ pub fn encoded_over_or_underflow() -> Vec { encode_revert("Panic(uint256)", &[uint_token(0x11)]) } +pub fn encoded_panic_out_of_bounds() -> Vec { + encode_revert("Panic(uint256)", &[uint_token(0x32)]) +} + pub fn encoded_div_or_mod_by_zero() -> Vec { encode_revert("Panic(uint256)", &[uint_token(0x12)]) } diff --git a/crates/tests/src/features.rs b/crates/tests/src/features.rs index 41d6577e25..e3a4dc2440 100644 --- a/crates/tests/src/features.rs +++ b/crates/tests/src/features.rs @@ -221,6 +221,37 @@ fn test_assert() { }) } +#[test] +fn test_arrays() { + with_executor(&|mut executor| { + let harness = deploy_contract(&mut executor, "arrays.fe", "Foo", &[]); + + harness.test_function( + &mut executor, + "get_from_memory", + &[uint_token(9)], + Some(&uint_token(10)), + ); + + validate_revert( + harness.capture_call(&mut executor, "get_from_memory", &[uint_token(10)]), + &encoded_panic_out_of_bounds(), + ); + + harness.test_function( + &mut executor, + "get_from_storage", + &[uint_token(9)], + Some(&uint_token(0)), + ); + + validate_revert( + harness.capture_call(&mut executor, "get_from_storage", &[uint_token(10)]), + &encoded_panic_out_of_bounds(), + ); + }) +} + #[rstest(fixture_file, input, expected, case("for_loop_with_static_array.fe", &[], uint_token(30)), case("for_loop_with_static_array_from_sto.fe", &[], uint_token(6)), diff --git a/crates/yulgen/src/constants.rs b/crates/yulgen/src/constants.rs index 1cfa63d10b..ad67bf67e6 100644 --- a/crates/yulgen/src/constants.rs +++ b/crates/yulgen/src/constants.rs @@ -64,6 +64,7 @@ pub fn numeric_min_max() -> HashMap pub const PANIC_FAILED_ASSERTION: usize = 0x01; pub const PANIC_OVER_OR_UNDERFLOW: usize = 0x11; pub const PANIC_DIV_OR_MOD_BY_ZERO: usize = 0x12; +pub const PANIC_OUT_OF_BOUNDS: usize = 0x32; pub const ERROR_INSUFFICIENT_FUNDS_TO_SEND_VALUE: usize = 0x100; pub const ERROR_FAILED_SEND_VALUE: usize = 0x101; diff --git a/crates/yulgen/src/operations/data.rs b/crates/yulgen/src/operations/data.rs index 6142c8f32f..17adb5f8be 100644 --- a/crates/yulgen/src/operations/data.rs +++ b/crates/yulgen/src/operations/data.rs @@ -139,5 +139,6 @@ pub fn indexed_array( index: yul::Expression, ) -> yul::Expression { let inner_size = literal_expression! { (typ.inner.size()) }; - expression! { add([array], (mul([index], [inner_size]))) } + let array_length = literal_expression! { (typ.size) }; + expression! { get_array_item([array], [array_length], [index], [inner_size] ) } } diff --git a/crates/yulgen/src/runtime/functions/data.rs b/crates/yulgen/src/runtime/functions/data.rs index 3e5f087cb9..57f486866b 100644 --- a/crates/yulgen/src/runtime/functions/data.rs +++ b/crates/yulgen/src/runtime/functions/data.rs @@ -1,3 +1,6 @@ +use crate::constants::PANIC_OUT_OF_BOUNDS; +use crate::operations::revert as revert_operations; + use yultsur::*; /// Return all data runtime functions @@ -15,6 +18,7 @@ pub fn all() -> Vec { ceil32(), cloadn(), free(), + get_array_item(), load_data_string(), map_value_ptr(), mcopym(), @@ -380,3 +384,16 @@ pub fn load_data_string() -> yul::Statement { } } } + +/// Returns a pointer to the array item at the requested index. +/// Reverts with a panic if the index is out of bounds. +pub fn get_array_item() -> yul::Statement { + function_definition! { + function get_array_item(array_ptr, array_length, index, inner_size) -> ptr { + (if (iszero((lt(index, array_length)))) { + [revert_operations::panic_revert(PANIC_OUT_OF_BOUNDS)] + } ) + (ptr := add(array_ptr, (mul(index, inner_size)))) + } + } +} diff --git a/newsfragments/606.feature.md b/newsfragments/606.feature.md new file mode 100644 index 0000000000..179b922454 --- /dev/null +++ b/newsfragments/606.feature.md @@ -0,0 +1,3 @@ +Added an out of bounds check for accessing array items. +If an array index is retrieved at an index that is not within +the bounds of the array it now reverts with `Panic(0x32)`.