Skip to content

Make string-typed mutable settings race-safe against concurrent reads - #161

Open
damilolaedwards wants to merge 1 commit into
ethpandaops:mainfrom
damilolaedwards:fix-settings-string-field-race
Open

Make string-typed mutable settings race-safe against concurrent reads#161
damilolaedwards wants to merge 1 commit into
ethpandaops:mainfrom
damilolaedwards:fix-settings-string-field-race

Conversation

@damilolaedwards

Copy link
Copy Markdown
Contributor

Problem

Every mutable setting is 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
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 internal
correctness 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 newLockedStringField
constructor. 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_RaceFree in
pkg/config/settings_fields_test.go, which drives Field.Set (the real
SetMany/recompute write path) against every field's real accessor
concurrently under -race — all nine pass clean.

go build, go vet, and go test -race ./pkg/... all pass (aside from a
pre-existing, unrelated failure in pkg/webui caused by the frontend not
being built in this checkout).

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.
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