Make string-typed mutable settings race-safe against concurrent reads - #161
Open
damilolaedwards wants to merge 1 commit into
Open
Make string-typed mutable settings race-safe against concurrent reads#161damilolaedwards wants to merge 1 commit into
damilolaedwards wants to merge 1 commit into
Conversation
Every mutable setting was mutated in place by SetMany/recompute with no lock, while modules holding the shared config read fields directly and unsynchronized. For the nine string-typed settings (extra data, reveal gate mode, bid candidate selection, and so on) a UI write racing a hot-path read could produce a torn pointer/length read and panic on an out-of-bounds slice. Since the WebUI and Builder API share the same HTTP port and the WebUI has no auth by default, this was reachable by any client that can reach --api-port, repeatably. Each of the nine fields now has an accessor method (or already had a normalizing one) that locks a mutex scoped to its owning config struct, and the settings write path takes the same lock. Every hot-path caller was switched from direct field access to the accessor. Scalar fields are unaffected: on the supported architectures a raced word-sized read can't produce an invalid memory access the way a torn string can, so this stays scoped to the fields that actually risk a crash.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Every mutable setting is mutated in place by
SetMany/recomputewith nolock, while modules holding the shared config read fields directly and
unsynchronized. For the nine string-typed settings (extra data, reveal gate
mode, bid candidate selection, and so on) a UI write racing a hot-path read
can produce a torn pointer/length read and panic on an out-of-bounds slice.
The WebUI and Builder API share the same HTTP port, and the WebUI has no
auth by default, so this is reachable by any client that can reach
--api-port— a repeatable remote crash primitive, not just an internalcorrectness bug.
Fix
Each of the nine string fields now has an accessor method (or already had a
normalizing one) that locks a mutex scoped to its owning config struct, and
the settings write path takes the same lock via a new
newLockedStringFieldconstructor. Every hot-path caller was switched from direct field access to
the accessor.
Scalar/bool fields are intentionally untouched: they still race under the Go
memory model, but on the supported architectures a raced word-sized read
can't produce an invalid memory access the way a torn string can, so this
stays scoped to the fields that actually risk a crash.
Testing
Added
TestLockedStringFields_RaceFreeinpkg/config/settings_fields_test.go, which drivesField.Set(the realSetMany/recomputewrite path) against every field's real accessorconcurrently under
-race— all nine pass clean.go build,go vet, andgo test -race ./pkg/...all pass (aside from apre-existing, unrelated failure in
pkg/webuicaused by the frontend notbeing built in this checkout).