Noir (Brillig), Heap Memory Corruption, CVE (Pending)

Listen to this Post

The vulnerability stems from a size calculation mismatch in the Brillig compiler when handling nested arrays of composite types returned by foreign calls. During compilation, the `allocate_foreign_call_result_array()` function incorrectly uses the semantic length of a nested array (e.g., 3 for [(u32, u32); 3]) instead of its semi-flattened size (6 memory slots for 3 tuples × 2 fields) when pre-allocating heap memory. This causes under-allocation: only 4 memory slots (3 data + 1 metadata) are allocated instead of the required 7 (6 data + 1 metadata). Consequently, when the VM writes the foreign call results (6 values + metadata), it overwrites adjacent heap memory, leading to corruption. The bug is triggered when a foreign call returns a nested array containing tuples or other composite types. The root cause is the use of `Type::Array(_, nested_size)` which discards the inner element types, failing to account for the flattened tuple fields. This mismatch results in heap corruption, potentially causing unpredictable behavior or crashes. The recommended fix is to multiply the semantic length by the number of element types when allocating nested arrays, i.e., use `types.len() nested_size` to compute the correct semi-flattened size.

Platform: Noir/Brillig
Version: All versions prior to fix
Vulnerability: Heap memory corruption
Severity: Critical
Date: 2026-04-21
Prediction: Expected patch within 30 days

What Undercode Say

The vulnerability is a classic case of type confusion in a compiler’s memory allocator. The root cause is the inconsistent handling of “semantic” vs “semi-flattened” array sizes, which is a subtle but critical flaw in the Brillig IR.
Bash command to check for vulnerable pattern in Noir source code:

grep -r "Type::Array(_, nested_size)" --include=".rs" noir/crates/noirc_evaluator/src/brillig/brillig_ir/

Rust code snippet demonstrating the vulnerable allocation path:

// Vulnerable code in allocate_foreign_call_result_array()
Type::Array(_, nested_size) => {
let inner_array = self.brillig_context.allocate_brillig_array(nested_size as usize);
// ...
}

Corrected version (using the helper function):

// Fixed code
Type::Array(elem_types, nested_size) => {
let total_slots = compute_array_length(elem_types, nested_size);
let inner_array = self.brillig_context.allocate_brillig_array(total_slots);
// ...
}

Exploit

An attacker can craft a Noir program that:

  1. Defines an unconstrained function that returns a nested array of tuples.

2. Calls this function via a foreign call.

  1. Observes heap corruption or crashes due to the under-allocated memory buffer.

Example Noir code that triggers the bug:

unconstrained fn get_nested_array() -> [(u32, u32); 3] {
[(1, 2), (3, 4), (5, 6)]
}
fn main() {
let _ = get_nested_array(); // Triggers heap corruption
}

Protection from this CVE

  • Upgrade to a patched version of Noir once available.
  • Avoid using foreign calls that return nested arrays of composite types until the patch is applied.
  • Monitor the official Noir GitHub repository for security advisories and updates.
  • Implement manual memory checks in custom Brillig code, ensuring that the allocated memory size matches the expected semi-flattened size.

Impact

  • Heap corruption in the Brillig VM, leading to:
  • Unpredictable behavior (crashes, data corruption).
  • Potential denial of service (program crashes).
  • Possible information disclosure (leakage of heap data).
  • Affects all Noir programs that use foreign calls with nested arrays of composite types.
  • Severity: Critical, as it undermines the memory safety of the Brillig VM.

🎯Let’s Practice Exploiting & Learn Patching For Free:

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top