pulsar v1.0.22: close AST indirect-dispatch hole on AlgebraicAggregate

Red's final v1.0.21 review flagged that TestAlgebraic_NoSkAccess only
walked CallExpr.Fun and a single SelectorExpr literal ("SignTo"). A
function-pointer assignment

    var fn = polyDeriveUniformLeqEta
    fn(seed, &p, 0, 4)

would bind to a bare *ast.Ident (no CallExpr.Fun banned-name match)
and slip through. Method-set dispatch like x.PolyDeriveUniformLeqEta(...)
would hit *ast.SelectorExpr but only "SignTo" was checked.

This patch extends the AST walker:
  - *ast.SelectorExpr: check n.Sel.Name against the full
    bannedFuncCalls list, not just the "SignTo" literal
  - *ast.Ident: ban bare references to any name in bannedFuncCalls
    (no legitimate use inside AlgebraicAggregate)

No current callsite is affected. This is pure defence-in-depth on the
public-BFT safety contract. BLOCKERS.md::PULSAR-V03-1 already CLOSED;
note added pointing at v1.0.22 + corrected stale 155/0/2 test-count
to actual 157/0/0.
This commit is contained in:
2026-05-24 15:15:39 -07:00
parent 1746519daf
commit c953ec8d8c
2 changed files with 37 additions and 1 deletions
+19 -1
View File
@@ -235,5 +235,23 @@ checks pass:
- `TestAlgebraic_NoSkAccess` PASS (public-BFT safety contract intact)
- `TestTransitional_DependsOnSkBytes` PASS (v0.2 still requires SkBytes;
the SkBytes-free path is v0.3 `AlgebraicAggregate`)
- Full pulsar suite: 155 PASS / 0 FAIL / 2 SKIP (-race green)
- Full pulsar suite: 157 PASS / 0 FAIL / 0 SKIP (-race green; the 2
SKIPs Blue's v1.0.20 report mentioned only appear under `-short` in
`shamir_gfq_test.go` and were not part of the v0.3 fix scope)
- Class N1 byte-equality preserved (`TestN1_ByteEquality_*` PASS)
**v1.0.21 hardened the regression guards** (Red feedback after v1.0.20):
`TestAMatrix_IsAlreadyInNTTDomain` now compares `km.a` (the field that
was wrong) at corners `[0][0]` and `[K-1][L-1]`, not just the
standalone `polyDeriveUniform` output. Stale "post-NTT" comments in
`threshold_v03_unsafediff_test.go` rewritten. AST banned-call list in
`TestAlgebraic_NoSkAccess` extended to include `polyDeriveUniformLeqEta`
and `polyDeriveUniformBounded` (defence-in-depth — secret-coefficient
samplers banned from `AlgebraicAggregate` body).
**v1.0.22 closed Red's final non-blocking finding**: AST guard now
also walks `*ast.SelectorExpr.Sel.Name` and `*ast.Ident` against the
banned-call list, so a function-pointer assignment
(`var fn = polyDeriveUniformLeqEta; fn(...)`) or method-set dispatch
can't bypass the `CallExpr.Fun` check. No current callsite is
affected; this closes the indirect-dispatch hole Red identified.
+18
View File
@@ -375,6 +375,24 @@ func TestAlgebraic_NoSkAccess(t *testing.T) {
if n.Sel.Name == "SignTo" {
t.Fatalf("AlgebraicAggregate body calls SignTo (%q) — public-BFT contract broken", selStr)
}
// Detect *.<banned> via method-set dispatch (interface/embedded).
for _, banned := range bannedFuncCalls {
if n.Sel.Name == banned {
t.Fatalf("AlgebraicAggregate body references banned selector %q via %q — public-BFT contract broken", banned, selStr)
}
}
case *ast.Ident:
// Detect bare references to banned identifiers, e.g.
// var fn = polyDeriveUniformLeqEta; fn(...)
// which a function-pointer or higher-order construct could
// use to bypass the CallExpr.Fun check. Any naked reference
// to a banned identifier inside the AlgebraicAggregate body
// is forbidden — there is no legitimate use.
for _, banned := range bannedFuncCalls {
if n.Name == banned {
t.Fatalf("AlgebraicAggregate body references banned identifier %q — public-BFT contract broken (function-pointer / indirect-dispatch path)", n.Name)
}
}
}
}
ast.Inspect(aggFunc.Body, func(node ast.Node) bool {