Skip to content

fix: serialize CreateCodeCache against Isolate.Dispose via RWMutex#9

Merged
yneves merged 1 commit into
mainfrom
fix/codecache-dispose-race
May 21, 2026
Merged

fix: serialize CreateCodeCache against Isolate.Dispose via RWMutex#9
yneves merged 1 commit into
mainfrom
fix/codecache-dispose-race

Conversation

@yneves
Copy link
Copy Markdown
Collaborator

@yneves yneves commented May 21, 2026

Summary

  • Fixes a SIGSEGV in UnboundScriptCreateCodeCache under concurrent multi-isolate workloads (12+ Playwright workers). snapshotDeserMu serialized NewIsolate/Dispose but left CreateCodeCache unprotected — it reads V8 shared-heap state (SharedFunctionInfo, StringForwardingTable, ReadOnlySpace roots) that Dispose tears down concurrently.
  • Upgrades snapshotDeserMu from sync.Mutex to sync.RWMutex: Dispose/NewIsolate take exclusive Lock(); CreateCodeCache takes shared RLock(). Multiple concurrent CreateCodeCache calls remain parallel.
  • Fixes a TOCTOU bug where i.ptr = nil was set after snapshotDeserMu.Unlock(), leaving a dangling-pointer window. Now set inside the lock.
  • Adds null guards in both Go (unbound_script.go) and C (unbound_script.cc) layers to prevent ISOLATE_SCOPE from dereferencing NULL.

Test plan

  • go vet ./... passes
  • TestUnboundScriptRun_OnlyInTheSameIsolate passes
  • All TestIsolate* and TestSnapshot* tests pass
  • Blindfox WPT sweep with 12 workers — no SIGSEGV in CreateCodeCache

V8 14.x shares process-global state between isolates (ReadOnlySpace,
StringForwardingTable, pointer compression cage). snapshotDeserMu
already serializes NewIsolate and Dispose against each other, but
CreateCodeCache was unprotected — it reads SharedFunctionInfo which
references shared-heap objects that Dispose can tear down concurrently.

Under 12-worker concurrency this causes SIGSEGV at address 0x0 in
UnboundScriptCreateCodeCache when the ISOLATE_SCOPE macro dereferences
a null or freed isolate pointer.

Changes:
- Upgrade snapshotDeserMu from sync.Mutex to sync.RWMutex.
  Dispose/NewIsolate take Lock (exclusive writer); CreateCodeCache
  takes RLock (shared reader). Multiple concurrent CreateCodeCache
  calls on different isolates remain parallel.
- Move i.ptr = nil inside the write lock in Dispose to close the
  TOCTOU window where a dangling pointer was visible after unlock.
- Add nil guards in CreateCodeCache (Go layer) before and after
  acquiring RLock, plus a null check in the C layer before
  ISOLATE_SCOPE to prevent dereferencing NULL.
@yneves yneves merged commit bd0df97 into main May 21, 2026
2 checks passed
@yneves yneves deleted the fix/codecache-dispose-race branch May 21, 2026 03: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