Skip to content

fix: per-room CSPRNG seed for unseeded rooms — the Ctx carried a shared zero#77

Merged
BCook98 merged 1 commit into
mainfrom
fix-wasm-room-seed
Jul 5, 2026
Merged

fix: per-room CSPRNG seed for unseeded rooms — the Ctx carried a shared zero#77
BCook98 merged 1 commit into
mainfrom
fix-wasm-room-seed

Conversation

@BCook98

@BCook98 BCook98 commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

The bug

Every production room of every wasm game plays the same "random" stream. The guest runtime (Go and Rust SDKs alike) seeds the SDK room PRNG from the CallContext seed verbatim (internal/game/run.go, rust/src/rt.rs) — but the host encoded the raw RoomConfig into the Ctx, and without --seed that is Seed=0, SeedSet=false. Result: rand.NewSource(0) / SplitMix64::new(0) in every room.

User-visible: the identical blackjack shoe (and blackjack-challenge shoe) in every room. Exploitable: pokies/scratchies/roulette outcomes are learnable by replaying a throwaway room, then betting the known stream in a fresh one.

The host already derived a per-room seed when SeedSet was false — but only fed it to the WASI entropy source, never the Ctx. And that derivation was the room-start timestamp, guessable to within a small search window.

The fix (host-side only)

wasmHandler.OnStart now draws one per-room seed from the host CSPRNG when !SeedSet and patches it into h.cfg.Seed, so every encoded Ctx and the WASI entropy source carry it. Deployed game binaries pick up the fix with no rebuild — both guest SDKs already consume the Ctx seed correctly; they were just handed a constant.

Explicitly seeded runs are untouched: --seed/-seed, conformance determinism checks, and hibernation restore (which already snapshots h.seed and restores it with SeedSet: true) all behave exactly as before.

Why nothing caught it

Every double derives a real seed: the native dev runner uses time.Now().UnixNano(), kittest and the conformance harness use explicit seeds, and the host-side roomHandle.Rand() derives FNV(roomID)^nano. Wasm-ABI-path-only — same blind spot as the casino zero-balance bug (#73).

Tests

The fixture gains a 'g' command logging r.Rand() (fixture.wasm rebuilt, keeping its v2.0.2 kit pin so it models deployed game binaries). host/gameabi/seed_test.go proves through the real wasm path:

  • TestUnseededRoomsGetDistinctGameRand — two production-shaped rooms must diverge (failed before the fix: both logged the seed-0 stream's first draw)
  • TestExplicitSeedKeepsGameRandDeterministic — same explicit seed reproduces the stream; different seeds diverge

ABI.md §4.1 now states the host obligation normatively: seed MUST be per-room unpredictable when the operator didn't request one.

Full suite + go vet + -race on gameabi: green. Changeset: kit patch.

🤖 Generated with Claude Code

…ed zero

The guest runtime (Go and Rust SDKs alike) seeds the room PRNG from the
CallContext seed verbatim, but the host encoded the raw RoomConfig into the
Ctx: without --seed, every room of every game received Seed=0 and drew the
one identical "random" stream. Identical blackjack shoes in every room, and
casino outcomes learnable by replaying a throwaway room.

The derived seed the host already computed for the WASI entropy source never
reached the Ctx — and was itself the room-start timestamp, guessable to
within a small search window. Both now come from one per-room CSPRNG draw,
patched into h.cfg at OnStart so every encoded Ctx carries it. Host-side
only: deployed game binaries pick up the fix without a rebuild. Explicit
seeds (--seed/-seed, conformance, hibernation restore) are unchanged.

The fixture gains a 'g' command logging r.Rand() so the regression test
proves the property through the real wasm path — two unseeded rooms must
diverge, same explicit seed must not. The doubles (kittest, TestRoom's own
rng, native devrun) all derive real seeds, which is why every existing test
missed this; ABI.md now states the host's obligation normatively.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@BCook98 BCook98 merged commit ec9f814 into main Jul 5, 2026
6 checks passed
@BCook98 BCook98 deleted the fix-wasm-room-seed branch July 5, 2026 11:27
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