From 2124c28486be80f7615d3b6d150b3c8eb5c5103d Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 25 Sep 2025 10:00:54 +0000 Subject: [PATCH 1/2] gate array OOB check in array assignment to brillig --- .../src/ssa/ssa_gen/context.rs | 12 ++- .../noirc_evaluator/src/ssa/ssa_gen/tests.rs | 96 +++++++++++++++++++ 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index de6edb147a1..8f5b8db303c 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -773,10 +773,14 @@ impl<'a> FunctionContext<'a> { // Checks for index Out-of-bounds match array_type { Type::Array(_, len) => { - let len = self - .builder - .numeric_constant(u128::from(*len), NumericType::length_type()); - self.codegen_access_check(index, len); + // Out of bounds array accesses are guaranteed to fail in ACIR so this check is performed implicitly. + // We then only need to inject it for brillig functions. + if self.builder.current_function.runtime().is_brillig() { + let len = self + .builder + .numeric_constant(u128::from(*len), NumericType::length_type()); + self.codegen_access_check(index, len); + } } _ => unreachable!("must have array or slice but got {array_type}"), } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs index 1276bf21944..29b3eeb4c20 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs @@ -101,3 +101,99 @@ fn basic_loop() { assert_normalized_ssa_equals(ssa, expected); } + +#[test] +fn acir_no_access_check_on_array_read() { + let src = " + fn main(mut array: [Field; 3], index: u32) -> pub Field { + array[index] + } + "; + let ssa = get_initial_ssa(src).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: [Field; 3], v1: u32): + v2 = allocate -> &mut [Field; 3] + store v0 at v2 + v3 = load v2 -> [Field; 3] + v4 = array_get v3, index v1 -> Field + return v4 + } + "; + assert_normalized_ssa_equals(ssa, expected); +} + +#[test] +fn acir_no_access_check_on_array_assignment() { + let src = " + fn main(mut array: [Field; 3], index: u32, x: Field, y: Field) { + array[index] = x; + } + "; + let ssa = get_initial_ssa(src).unwrap(); + + let expected = " + acir(inline) fn main f0 { + b0(v0: [Field; 3], v1: u32, v2: Field): + v3 = allocate -> &mut [Field; 3] + store v0 at v3 + v4 = load v3 -> [Field; 3] + v5 = array_set v4, index v1, value v2 + v7 = unchecked_add v1, u32 1 + store v5 at v3 + return + } + "; + assert_normalized_ssa_equals(ssa, expected); +} + +#[test] +fn brillig_access_check_on_array_read() { + let src = " + unconstrained fn main(mut array: [Field; 3], index: u32) -> pub Field { + array[index] + } + "; + let ssa = get_initial_ssa(src).unwrap(); + + let expected = r#" + brillig(inline) fn main f0 { + b0(v0: [Field; 3], v1: u32): + v2 = allocate -> &mut [Field; 3] + store v0 at v2 + v3 = load v2 -> [Field; 3] + v5 = lt v1, u32 3 + constrain v5 == u1 1, "Index out of bounds" + v7 = array_get v3, index v1 -> Field + return v7 + } + "#; + assert_normalized_ssa_equals(ssa, expected); +} + +#[test] +fn brillig_access_check_on_array_assignment() { + let src = " + unconstrained fn main(mut array: [Field; 3], index: u32, x: Field) { + array[index] = x; + } + "; + let ssa = get_initial_ssa(src).unwrap(); + + let expected = r#" + brillig(inline) fn main f0 { + b0(v0: [Field; 3], v1: u32, v2: Field): + v3 = allocate -> &mut [Field; 3] + store v0 at v3 + v4 = load v3 -> [Field; 3] + v6 = lt v1, u32 3 + constrain v6 == u1 1, "Index out of bounds" + v8 = array_set v4, index v1, value v2 + v10 = unchecked_add v1, u32 1 + store v8 at v3 + return + } + "#; + assert_normalized_ssa_equals(ssa, expected); +} From 9bdf2c66d272a387e3bdcf47f2a6ea179e79405d Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Thu, 25 Sep 2025 10:01:08 -0400 Subject: [PATCH 2/2] Update compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs --- compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs index 29b3eeb4c20..e6653c731d1 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/tests.rs @@ -127,7 +127,7 @@ fn acir_no_access_check_on_array_read() { #[test] fn acir_no_access_check_on_array_assignment() { let src = " - fn main(mut array: [Field; 3], index: u32, x: Field, y: Field) { + fn main(mut array: [Field; 3], index: u32, x: Field) { array[index] = x; } ";