Description
Noir is a Domain Specific Language for SNARK proving systems that is designed to use any ACIR compatible proving system, and Brillig is the bytecode ACIR uses for non-determinism. Noir programs can invoke external functions through foreign calls. When compiling to Brillig bytecode, the SSA instructions are processed block-by-block in `BrilligBlock::compile_block()`. When the compiler encounters an `Instruction::Call` with a `Value::ForeignFunction` target, it invokes `codegen_call()` in `brillig_call/code_gen_call.rs`, which dispatches to `convert_ssa_foreign_call()`. Before emitting the foreign call opcode, the compiler must pre-allocate memory for any array results the call will return. This happens through `allocate_external_call_results()`, which iterates over the result types. For `Type::Array` results, it delegates to `allocate_foreign_call_result_array()` to recursively allocate memory on the heap for nested arrays. The `BrilligArray` struct is the internal representation of a Noir array in Brillig IR. Its `size` field represents the semi-flattened size, the total number of memory slots the array occupies, accounting for the fact that composite types like tuples consume multiple slots per element. This size is computed by `compute_array_length()` in `brillig_block_variables.rs`. For the outer array, `allocate_external_call_results()` correctly uses `define_variable()`, which internally calls `allocate_value_with_type()`. This function applies the formula above, producing the correct semi-flattened size. However, for nested arrays, `allocate_foreign_call_result_array()` contains a bug. The pattern `Type::Array(_, nested_size)` discards the inner types with `_` and uses only `nested_size`, the semantic length of the nested array (the number of logical elements), not the semi-flattened size. For simple element types this works correctly, but for composite element types it under-allocates. Foreign calls returning nested arrays of tuples or other composite types corrupt the Brillig VM heap. Version 1.0.0-beta.19 fixes this issue.
Published: 2026-04-23
Score: 9.3 Critical
EPSS: < 1% Very Low
KEV: No
Impact: Remote Code Execution
Action: Immediate Patch
AI Analysis

Impact

A flaw in the Brillig bytecode compiler for the Noir language causes heap corruption when allocating memory for nested array results returned by foreign function calls. The bug under‑allocates space for arrays whose elements are composite types, such as tuples, leading to a buffer overrun during code generation. The nature of the flaw implies that an attacker can influence program state, potentially enabling arbitrary code execution or causing compiler crashes. Based on the description, it is inferred that an attacker would need to supply compiled Noir code containing a foreign call that returns a nested array of composite types to trigger the corruption.

Affected Systems

The vulnerability exists in the Noir compiler (noir‑lang:noir). All releases prior to version 1.0.0‑beta.19 are affected. The issue is fixed in version 1.0.0‑beta.19 and later.

Risk and Exploitability

The CVSS score of 9.3 indicates critical severity. The EPSS score of less than 1% suggests that exploitation is currently considered unlikely. The vulnerability is not listed in the CISA KEV catalog. Attackers would need to supply compiled Noir code that includes a foreign call returning a nested array of composite types, a condition that is inferred from the description rather than explicitly stated. The impact is limited to the compilation process, although erroneous compilation can propagate into runtime bugs that might be exploitable by malicious contracts or programs.

Generated by OpenCVE AI on April 28, 2026 at 15:03 UTC.

Remediation

No vendor fix or workaround currently provided.

OpenCVE Recommended Actions

  • Upgrade the Noir compiler to version 1.0.0‑beta.19 or newer, which contains the fix for the under‑allocation bug.
  • Recompile all existing Noir contracts and code with the updated compiler to ensure correct memory allocation for foreign calls.
  • Validate that foreign function calls return expected values after recompilation and monitor the system for any unexpected behavior or crashes.

Generated by OpenCVE AI on April 28, 2026 at 15:03 UTC.

Tracking

Sign in to view the affected projects.

Advisories
Source ID Title
Github GHSA Github GHSA GHSA-jj7c-x25r-r8r3 Brillig: Heap corruption in foreign call results with nested tuple arrays
History

Tue, 28 Apr 2026 09:45:00 +0000

Type Values Removed Values Added
First Time appeared Noir-lang
Noir-lang noir
Vendors & Products Noir-lang
Noir-lang noir

Thu, 23 Apr 2026 13:15:00 +0000

Type Values Removed Values Added
Metrics ssvc

{'options': {'Automatable': 'yes', 'Exploitation': 'none', 'Technical Impact': 'total'}, 'version': '2.0.3'}


Thu, 23 Apr 2026 01:15:00 +0000

Type Values Removed Values Added
Description Noir is a Domain Specific Language for SNARK proving systems that is designed to use any ACIR compatible proving system, and Brillig is the bytecode ACIR uses for non-determinism. Noir programs can invoke external functions through foreign calls. When compiling to Brillig bytecode, the SSA instructions are processed block-by-block in `BrilligBlock::compile_block()`. When the compiler encounters an `Instruction::Call` with a `Value::ForeignFunction` target, it invokes `codegen_call()` in `brillig_call/code_gen_call.rs`, which dispatches to `convert_ssa_foreign_call()`. Before emitting the foreign call opcode, the compiler must pre-allocate memory for any array results the call will return. This happens through `allocate_external_call_results()`, which iterates over the result types. For `Type::Array` results, it delegates to `allocate_foreign_call_result_array()` to recursively allocate memory on the heap for nested arrays. The `BrilligArray` struct is the internal representation of a Noir array in Brillig IR. Its `size` field represents the semi-flattened size, the total number of memory slots the array occupies, accounting for the fact that composite types like tuples consume multiple slots per element. This size is computed by `compute_array_length()` in `brillig_block_variables.rs`. For the outer array, `allocate_external_call_results()` correctly uses `define_variable()`, which internally calls `allocate_value_with_type()`. This function applies the formula above, producing the correct semi-flattened size. However, for nested arrays, `allocate_foreign_call_result_array()` contains a bug. The pattern `Type::Array(_, nested_size)` discards the inner types with `_` and uses only `nested_size`, the semantic length of the nested array (the number of logical elements), not the semi-flattened size. For simple element types this works correctly, but for composite element types it under-allocates. Foreign calls returning nested arrays of tuples or other composite types corrupt the Brillig VM heap. Version 1.0.0-beta.19 fixes this issue.
Title Brillig: Heap corruption in foreign call results with nested tuple arrays
Weaknesses CWE-131
References
Metrics cvssV4_0

{'score': 9.3, 'vector': 'CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N'}


cve-icon MITRE

Status: PUBLISHED

Assigner: GitHub_M

Published:

Updated: 2026-04-25T03:55:38.608Z

Reserved: 2026-04-18T02:51:52.973Z

Link: CVE-2026-41197

cve-icon Vulnrichment

Updated: 2026-04-23T12:32:55.559Z

cve-icon NVD

Status : Deferred

Published: 2026-04-23T02:16:18.127

Modified: 2026-04-29T20:46:33.890

Link: CVE-2026-41197

cve-icon Redhat

No data.

cve-icon OpenCVE Enrichment

Updated: 2026-04-28T15:15:34Z

Weaknesses