Skip to content

fix(bb): avoid out-of-bounds read in DetectsUnconstrainedWitnesses#24783

Draft
AztecBot wants to merge 1 commit into
nextfrom
cb/fix-nightly-debug-acir-components-check
Draft

fix(bb): avoid out-of-bounds read in DetectsUnconstrainedWitnesses#24783
AztecBot wants to merge 1 commit into
nextfrom
cb/fix-nightly-debug-acir-components-check

Conversation

@AztecBot

Copy link
Copy Markdown
Collaborator

Fixes the Nightly Debug Build failure on next (commit 519f637).

Symptom

The nightly job exited 134 (SIGABRT). The GitHub Actions log only carries the SSM wrapper; the CI dashboard log shows the build itself succeeded and a single test aborted:

FAILED: run_test.sh acir_components_check_tests AcirComponentsCheckTest.DetectsUnconstrainedWitnesses (code: 134)

with (test log):

/usr/include/c++/13/debug/vector:442:
  reference std::vector<unsigned int>::operator[](size_type)
Error: attempt to subscript container with out-of-bounds index 10, but
container only holds 9 elements.

Root cause

The test corrupted the builder to force the UNCONSTRAINED path:

auto builder = create_circuit<AcirComponentsCheckBuilder>(program);
// Corrupt the circuit
builder.real_variable_index.resize(9);

That breaks a builder invariant: gates still reference variable index 10, but real_variable_index now holds 9 entries. ComponentsChecker::build_circuit_component_map runs cdg::StaticAnalyzer_ over the builder before its own bounds guard, and the analyzer indexes real_variable_index for every gate wire — e.g. check_is_not_constant_variable:

uint32_t real_variable_index = circuit_builder.real_variable_index[variable_index];

So the truncation makes the analyzer read out of bounds. This is a test bug, not a product bug — the checker's own accesses to real_variable_index are already guarded, and a real builder is never internally inconsistent this way.

It only fails at night because the debug preset compiles with -D_GLIBCXX_DEBUG (barretenberg/cpp/CMakePresets.json). Release/default builds use unchecked operator[], so the same read silently returns garbage and the test passes. Nothing regressed recently in the analyzer; the corrupting line arrived with the acir_components_check import, which reached public next on 2026-07-14.

Fix

Exercise the same code path through a mismatch that can actually occur, leaving the builder consistent: build the circuit from one linked pair, then check it against an ACIR circuit that additionally references witnesses 100/101, which never become circuit variables. Those hit the checker's witness >= real_variable_index.size() guard and are reported as UNCONSTRAINED — which is the behavior the test is meant to pin.

Testing

Reproduced and verified locally with the same preset as the nightly (cmake --preset debug -DAVM=OFF):

  • Before: AcirComponentsCheckTest.DetectsUnconstrainedWitnesses aborts with the identical out-of-bounds index 10, but container only holds 9 elements message, exit 134.
  • After: passes; all 10 tests in acir_components_check_tests pass.
  • Full debug suite green: NATIVE_PRESET=debug AVM=0 NO_FAIL_FAST=1 ./bootstrap.sh test from barretenberg/cpp completed with no failures (the nightly fails fast, so this confirms no second debug-only failure was hiding behind the first).

Note on the base branch

Targeting next as the nightly-failure dispatch requested, rather than the usual merge-train/barretenberg base for barretenberg/**, so the nightly recovers without waiting on the train. Happy to retarget if you'd prefer it go through the merge train.


Created by claudebox · group: slackbot · Slack thread

The test corrupted the builder with real_variable_index.resize(9) while its
gates still referenced variable index 10. The static analyzer indexes
real_variable_index for every gate wire, so it read past the end. Release
builds silently read garbage; the nightly debug preset compiles with
-D_GLIBCXX_DEBUG, which turns the read into an abort (exit 134).

Drive the UNCONSTRAINED path through a real mismatch instead: build the
circuit from one linked pair and check it against an ACIR circuit that also
references witnesses 100/101, which never become circuit variables. The
builder stays internally consistent.
@AztecBot AztecBot added ci-barretenberg Run all barretenberg/cpp checks. ci-draft Run CI on draft PRs. ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure claudebox Owned by claudebox. it can push to this PR. labels Jul 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-barretenberg Run all barretenberg/cpp checks. ci-draft Run CI on draft PRs. ci-no-fail-fast Sets NO_FAIL_FAST in the CI so the run is not aborted on the first failure claudebox Owned by claudebox. it can push to this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant