Skip to content

fix(keysharecore): guard Core.trustedKeys with an RWMutex (#594) - #595

Open
dobby-coder[bot] wants to merge 3 commits into
masterfrom
fix/keysharecore-trustedkeys-race-594
Open

fix(keysharecore): guard Core.trustedKeys with an RWMutex (#594)#595
dobby-coder[bot] wants to merge 3 commits into
masterfrom
fix/keysharecore-trustedkeys-race-594

Conversation

@dobby-coder

@dobby-coder dobby-coder Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes the data race on the unsynchronized Core.trustedKeys map reported in #594. trustedKeys was the only one of the three maps on the Core struct without a mutex, yet it is written at runtime by DangerousAddTrustedPublicKey (registered as a Configuration UpdateListener, so it fires on the request path on every scheme update) and read concurrently by the Generate* request handlers. A concurrent read+write on a built-in Go map is a fatal concurrent map read and map write panic that crashes the whole keyshare server.

Closes #594

Changes

  • internal/keysharecore/core.go
    • Add trustedKeysMutex sync.RWMutex next to trustedKeys, mirroring the existing commitmentMutex / authChallengesMutex pattern.
    • Take the write lock in DangerousAddTrustedPublicKey.
  • internal/keysharecore/operations.go — take a read lock around every trustedKeys access in the four readers:
    • GeneratePs (builds the key list under the read lock)
    • GenerateCommitments (builds the key list under the read lock)
    • GenerateResponse (single lookup under the read lock)
    • GenerateResponseV2 — looks up the requested key and snapshots the trusted-keys map under the read lock, then passes the snapshot to gabi.KeyshareResponse, so the callee never reads the live map.
  • internal/keysharecore/operations_test.go
    • New -race regression test TestTrustedKeysConcurrentAccess that runs GeneratePs/GenerateCommitments readers concurrently with DangerousAddTrustedPublicKey writers.

The read locks are held only around the map access (key lookup / snapshot), not across verifyAccess or the proof crypto, so the change adds no contention on the hot path beyond the map read itself.

Testing

  • go build ./... — clean
  • go vet ./... — clean
  • go test -race ./internal/keysharecore/ — passes
  • Verified the new test genuinely catches the bug: reverting only the locking (keeping the test) makes go test -race -run TestTrustedKeysConcurrentAccess report DATA RACE and fail; with the fix it passes.

Note: irmago CI runs the test suite without -race, and the keyshare / sessiontest suites need docker-backed services (postgres/redis/scheme servers) that aren't available in this environment, so full suite execution is deferred to CI. The keysharecore package tests above run standalone and were executed locally under -race.
The eudi/storage/db/sqlcipher package needs a native sqlcipher library that isn't installable in this workspace; it is unrelated to this change, which only touches internal/keysharecore.

Core.trustedKeys was an unsynchronized map that is written at runtime by
DangerousAddTrustedPublicKey (registered as a Configuration UpdateListener,
so it fires on the request path on every scheme update) and read concurrently
by the Generate* request handlers. Concurrent read+write on a built-in map is
a fatal "concurrent map read and map write" panic that crashes the keyshare
server.

Add a sync.RWMutex (mirroring the sibling commitmentMutex/authChallengesMutex):
take the write lock in DangerousAddTrustedPublicKey and a read lock in GeneratePs,
GenerateCommitments, GenerateResponse and GenerateResponseV2. GenerateResponseV2
now snapshots the trusted keys under the read lock before handing them to
gabi.KeyshareResponse, so the callee never touches the live map.

Add a -race regression test (TestTrustedKeysConcurrentAccess) that hammers the
readers concurrently with the writer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE. Posted as a COMMENT only because GitHub blocks a bot from approving its own PR — treat this as a clean sign-off, not withholding.

✅ Race fix on Core.trustedKeys is correct and complete; no blocking issues.

Rule compliance: clean across all checked rules (PR-hygiene, draft/test handling, irmago repo-specific Go rules). Conventional commit title, regression test added for a fix, no ST1005 error-string issues, PR body matches the diff.

Code review: the sync.RWMutex mirrors the existing commitmentMutex/authChallengesMutex pattern. Write-lock in DangerousAddTrustedPublicKey; read-locks in all four readers with RUnlock on every path (including the in-loop ErrKeyNotFound early returns in GeneratePs/GenerateCommitments). GenerateResponseV2 snapshots the map under the read lock and passes the copy to gabi.KeyshareResponse, so the live map is never read concurrently. No lock is held across verifyAccess or proof crypto. The new -race test was verified to fail with DATA RACE on revert and pass with the fix.

Deferral of keyshare/sessiontest suites to CI (docker-backed services) is correctly noted in the PR body.

@dobby-coder

dobby-coder Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

/dobby fix — The lint and changelog CI checks are failing on this PR (test, build, CodeQL, integration all pass). For lint: go fix -diff reports internal/keysharecore/operations.go needs a goroutine-closure syntax fix — the diff replaces }() with }) in the t.Run callback at the WaitGroup loop. For changelog: add an entry under the Unreleased section of CHANGELOG.md describing the RWMutex guard on Core.trustedKeys (or apply the skip-changelog label if a maintainer judges no entry is warranted). Get both lanes green.

@rubenhensen
rubenhensen self-requested a review July 22, 2026 06:28
rubenhensen
rubenhensen previously approved these changes Jul 22, 2026
@rubenhensen

Copy link
Copy Markdown
Contributor

/dobby fix the CI blocks

@dobby-coder

dobby-coder Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Hey Ruben, I'm on it — digging into the CI failures on #595 now and I'll push fixes to this PR as I sort them out.

Resolve the two failing CI lanes on this PR:
- lint: apply the `go fix` modernizers on the files this PR touched
  (maps.Copy for the trustedKeys snapshot in operations.go, and
  sync.WaitGroup.Go / for-range loops in the new race test), so
  `go fix -diff ./...` is clean.
- changelog: add the Fixed entry under Unreleased for the RWMutex guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@dobby-coder dobby-coder Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VERDICT: approve

Rules Dobby 2 sign-off — clean, no blocking issues. Posted as a COMMENT because GitHub blocks a bot from formally approving its own PR; treat this as a full sign-off.

CI blocks resolved. The last commit (apply go modernizers and add changelog entry) fixed what was red: the changelog check now passes (Unreleased entry added) and lint/build/analyze/CodeQL/integration are all green. Only the test job is still running.

Rule compliance: clean across all checked rules.

  • Conventional-commit title with a valid scope (fix(keysharecore): …).
  • PR body links the issue with Closes #594 on its own line — auto-closes on merge.
  • Regression test committed for the fix (TestTrustedKeysConcurrentAccess, -race).
  • No Go struct/pointer equality traps; CHANGELOG prose is plain and honest.

Code review: the sync.RWMutex mirrors the existing commitmentMutex/authChallengesMutex pattern. Write-lock in DangerousAddTrustedPublicKey; read-locks in all four readers with RUnlock on every path (including the in-loop ErrKeyNotFound early returns). GenerateResponseV2 snapshots the map under the read lock via maps.Copy and passes the copy to gabi.KeyshareResponse, so the live map is never read concurrently. No lock is held across verifyAccess or proof crypto.

Nothing to fix.

@dobby-coder

dobby-coder Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Done — CI blocks are cleared on #595. The last commit added the missing CHANGELOG entry and applied the go modernizer fixes, so changelog, lint, build, analyze, CodeQL, and the integration suites are all green now.

While I was at it I also ran a full code review of the trustedKeys RWMutex fix and posted it as a comment with VERDICT: approve (zero findings — GitHub won't let a bot formally approve its own PR). The test job was still running as of my last check, but everything else is clear and it's ready-for-review whenever you want to merge.

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.

fix: data race on unsynchronized Core.trustedKeys map can crash keyshare server

1 participant