Skip to content

fix(graph): fvc_search self-protects its FVC_MAX_VERTICES stack buffers (RG-2 #2) - #74

Merged
stblake merged 1 commit into
mainfrom
fvc-search-cap-guard
Sep 1, 2026
Merged

fix(graph): fvc_search self-protects its FVC_MAX_VERTICES stack buffers (RG-2 #2)#74
stblake merged 1 commit into
mainfrom
fvc-search-cap-guard

Conversation

@stblake

@stblake stblake commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Summary

Closes RG-2 adversarial finding #2 (from PR #73): fvc_search is exported in
src/graph/graph.h, and its inner fvc_bb uses stack arrays
char seen[FVC_MAX_VERTICES + 2] / char forbid[FVC_MAX_VERTICES + 2], indexed
up to used + 1 as the search deepens. The 128-vertex cap that keeps those
indices in bounds was enforced only in the head builtin_find_vertex_coloring,
never in fvc_search — and because the symbol is exported while
FVC_MAX_VERTICES is not, a cross-TU caller passing n > 128 could not discover
the precondition and would overflow the buffers.

Fix (one line + comment)

fvc_search now refuses (return 0) when n > FVC_MAX_VERTICES, so the
invariant lives with the buffers it protects rather than in an untrusted caller.
return 0 is the existing "cannot prove minimality, refuse" contract, so the
head leaves the expression unevaluated exactly as it already does for n > 128.

No behaviour change on the head pathbuiltin_find_vertex_coloring already
filters n > 128 before calling in, so every graph that reaches fvc_search
through the head has n ≤ 128 and is unaffected.

Test

A regression assertion in test_vertex_coloring_internals: a direct
fvc_search on CompleteGraph[129] must return 0 with zero steps. Without the
guard it returns 129 via the lb >= ub short-circuit, so the assertion fails
when the guard is absent — a genuine regression check, not a tautology.

Verification

  • Default build clean (-Werror gate set).
  • graph_tests green — 17 tests incl. test_vertex_coloring / test_vertex_coloring_internals (true exit 0, captured unpiped).
  • make check-c99, make check-packed-aware pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_018GNB6E1ftx4TkAk47VfkNJ

Closes the RG-2 adversarial finding #2. fvc_search is exported in graph.h and
its inner fvc_bb uses stack arrays seen[]/forbid[] sized to FVC_MAX_VERTICES
(128). The cap was enforced only in the head builtin_find_vertex_coloring, not
in fvc_search, and FVC_MAX_VERTICES is not visible in graph.h -- so a cross-TU
caller passing n > 128 could not discover the precondition and would overflow
those buffers. fvc_search now refuses (return 0) when n > FVC_MAX_VERTICES,
locating the invariant with the buffers it guards. No behaviour change on the
head path, which already filters n > 128 before calling in.

Regression: a direct fvc_search on CompleteGraph[129] must return 0 with zero
steps (test_vertex_coloring_internals). Without the guard it returns 129 via the
lb >= ub short-circuit, so the assertion genuinely fails when the guard is absent.

Verified: default build clean; graph_tests green (17 tests); make check-c99 and
make check-packed-aware pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018GNB6E1ftx4TkAk47VfkNJ
@stblake
stblake merged commit f4415c2 into main Sep 1, 2026
2 checks passed
@stblake
stblake deleted the fvc-search-cap-guard branch September 1, 2026 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant