From 6a151bd4464fdfba44c0afbbc97ee47806fd5b4d Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 8 Jul 2026 17:50:16 +0800 Subject: [PATCH] fix(bbs): eliminate data race in f2192 curve cache f2192 lazily populated a package-level map with no synchronization, so concurrent first-use calls (e.g. Verify and AuditNymEid, both through FrFromOKM) raced on the map, failing -race builds and risking a fatal concurrent map access. Precompute the entry for every registered curve at package init and treat the map as read-only afterwards; a curve not in ml.Curves falls back to computing the value without caching. Signed-off-by: Evan --- bbs/fr.go | 14 ++++++++++---- bbs/fr_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 bbs/fr_test.go diff --git a/bbs/fr.go b/bbs/fr.go index 1fa10a8..0236f05 100644 --- a/bbs/fr.go +++ b/bbs/fr.go @@ -25,17 +25,23 @@ var ( 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, } - f2192Cache = make(map[*ml.Curve]*ml.Zr) + //nolint:gochecknoglobals + f2192Cache = func() map[*ml.Curve]*ml.Zr { + m := make(map[*ml.Curve]*ml.Zr, len(ml.Curves)) + for _, c := range ml.Curves { + m[c] = c.NewZrFromBytes(f2192Bytes) + } + + return m + }() ) func f2192(curve *ml.Curve) *ml.Zr { if cached, ok := f2192Cache[curve]; ok { return cached } - val := curve.NewZrFromBytes(f2192Bytes) - f2192Cache[curve] = val - return val + return curve.NewZrFromBytes(f2192Bytes) } func FrFromOKM(message []byte, curve *ml.Curve) *ml.Zr { diff --git a/bbs/fr_test.go b/bbs/fr_test.go new file mode 100644 index 0000000..9eff3c1 --- /dev/null +++ b/bbs/fr_test.go @@ -0,0 +1,39 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bbs + +import ( + "sync" + "testing" + + ml "github.com/IBM/mathlib" + "github.com/stretchr/testify/require" +) + +func TestFrFromOKMConcurrent(t *testing.T) { + message := []byte("test message") + + for i, curve := range ml.Curves { + const workers = 16 + results := make([]*ml.Zr, workers) + + var wg sync.WaitGroup + wg.Add(workers) + for w := range workers { + go func(w int) { + defer wg.Done() + results[w] = FrFromOKM(message, curve) + }(w) + } + wg.Wait() + + expected := FrFromOKM(message, curve) + for w, r := range results { + require.True(t, expected.Equals(r), "curve %d, worker %d", i, w) + } + } +}