From 8c2fe02785079ebc73157512fd624b1c44bfead3 Mon Sep 17 00:00:00 2001 From: AztecBot Date: Wed, 6 May 2026 06:48:59 +0000 Subject: [PATCH 1/2] fix(bb): clamp BatchMergeProver degree-check loop to avoid OOB in debug --- .../src/barretenberg/goblin/batch_merge_prover.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/goblin/batch_merge_prover.cpp b/barretenberg/cpp/src/barretenberg/goblin/batch_merge_prover.cpp index 7245b05b1990..4713b4f4bf9e 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/batch_merge_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/goblin/batch_merge_prover.cpp @@ -33,11 +33,17 @@ typename BatchMergeProver::Polynomial BatchMergeProver::compute_degree_check_pol reversed_columns.emplace_back(poly.reverse()); } + // The two inputs only mismatch when the prover is invoked in a misuse path that the + // BB_ASSERT_LTE(N, M, ...) check in construct_proof would normally catch — e.g. the + // TooManySubtablesFails test bypasses asserts to exercise verifier rejection. In that case + // flattened_columns is sized for N (= num_subtables) but degree_check_challenges is sized for + // M (= max_subtables) < N, and we must clamp to avoid an out-of-bounds read on the challenges. + const size_t num_terms = std::min(flattened_columns.size(), degree_check_challenges.size()); std::vector> reversed_column_spans; std::vector scalars; - reversed_column_spans.reserve(flattened_columns.size()); - scalars.reserve(flattened_columns.size()); - for (size_t idx = 0; idx < flattened_columns.size(); ++idx) { + reversed_column_spans.reserve(num_terms); + scalars.reserve(num_terms); + for (size_t idx = 0; idx < num_terms; ++idx) { reversed_column_spans.emplace_back(reversed_columns[idx]); scalars.push_back(degree_check_challenges[idx]); } From 0a5b07cccd84fb58ff33aa3f263db3ebe286fcfc Mon Sep 17 00:00:00 2001 From: federicobarbacovi <171914500+federicobarbacovi@users.noreply.github.com> Date: Thu, 7 May 2026 08:44:31 +0000 Subject: [PATCH 2/2] Reset prover, fix test --- .../src/barretenberg/goblin/batch_merge.test.cpp | 14 ++++++++++++++ .../src/barretenberg/goblin/batch_merge_prover.cpp | 12 +++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/barretenberg/cpp/src/barretenberg/goblin/batch_merge.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/batch_merge.test.cpp index ce0834b66282..0eca2dc39090 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/batch_merge.test.cpp +++ b/barretenberg/cpp/src/barretenberg/goblin/batch_merge.test.cpp @@ -238,8 +238,22 @@ class TweakableBatchMergeProver : public BatchMergeProver { } // Step 6: degree-check poly + bool is_too_many_subtables = flattened_cols.size() > num_degree_check_challenges; + if (is_too_many_subtables) { + // This is the case in which we test that if the prover sends more columns than the max number of tables + // then the verifier rejects + degree_check_challenges.push_back(degree_check_challenges.back() * degree_check_challenge); + } + Polynomial degree_check_poly = compute_degree_check_polynomial(flattened_cols, degree_check_challenges, max_shift_size); + + if (is_too_many_subtables) { + // Remove the extra challenge added above to keep the degree check poly consistent with the rest of the + // proof + degree_check_challenges.pop_back(); + } + if (fault_mode == FaultMode::BAD_DEGREE_CHECK_POLY && !degree_check_poly.is_empty()) { degree_check_poly.at(0) += FF(1); } diff --git a/barretenberg/cpp/src/barretenberg/goblin/batch_merge_prover.cpp b/barretenberg/cpp/src/barretenberg/goblin/batch_merge_prover.cpp index 4713b4f4bf9e..7245b05b1990 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/batch_merge_prover.cpp +++ b/barretenberg/cpp/src/barretenberg/goblin/batch_merge_prover.cpp @@ -33,17 +33,11 @@ typename BatchMergeProver::Polynomial BatchMergeProver::compute_degree_check_pol reversed_columns.emplace_back(poly.reverse()); } - // The two inputs only mismatch when the prover is invoked in a misuse path that the - // BB_ASSERT_LTE(N, M, ...) check in construct_proof would normally catch — e.g. the - // TooManySubtablesFails test bypasses asserts to exercise verifier rejection. In that case - // flattened_columns is sized for N (= num_subtables) but degree_check_challenges is sized for - // M (= max_subtables) < N, and we must clamp to avoid an out-of-bounds read on the challenges. - const size_t num_terms = std::min(flattened_columns.size(), degree_check_challenges.size()); std::vector> reversed_column_spans; std::vector scalars; - reversed_column_spans.reserve(num_terms); - scalars.reserve(num_terms); - for (size_t idx = 0; idx < num_terms; ++idx) { + reversed_column_spans.reserve(flattened_columns.size()); + scalars.reserve(flattened_columns.size()); + for (size_t idx = 0; idx < flattened_columns.size(); ++idx) { reversed_column_spans.emplace_back(reversed_columns[idx]); scalars.push_back(degree_check_challenges[idx]); }