mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-04 08:51:27 +01:00
syncs: add means of declare locking assumptions for debug mode validation
Updates #17852 Change-Id: I42a64a990dcc8f708fa23a516a40731a19967aba Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
3f9f0ed93c
commit
74ed589042
@ -876,6 +876,7 @@ func (b *LocalBackend) initPrefsFromConfig(conf *conffile.Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) setStaticEndpointsFromConfigLocked(conf *conffile.Config) {
|
func (b *LocalBackend) setStaticEndpointsFromConfigLocked(conf *conffile.Config) {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
if conf.Parsed.StaticEndpoints == nil && (b.conf == nil || b.conf.Parsed.StaticEndpoints == nil) {
|
if conf.Parsed.StaticEndpoints == nil && (b.conf == nil || b.conf.Parsed.StaticEndpoints == nil) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -894,6 +895,7 @@ func (b *LocalBackend) setStaticEndpointsFromConfigLocked(conf *conffile.Config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) setStateLocked(state ipn.State) {
|
func (b *LocalBackend) setStateLocked(state ipn.State) {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
if b.state == state {
|
if b.state == state {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -906,6 +908,7 @@ func (b *LocalBackend) setStateLocked(state ipn.State) {
|
|||||||
// setConfigLocked uses the provided config to update the backend's prefs
|
// setConfigLocked uses the provided config to update the backend's prefs
|
||||||
// and other state.
|
// and other state.
|
||||||
func (b *LocalBackend) setConfigLocked(conf *conffile.Config) error {
|
func (b *LocalBackend) setConfigLocked(conf *conffile.Config) error {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
p := b.pm.CurrentPrefs().AsStruct()
|
p := b.pm.CurrentPrefs().AsStruct()
|
||||||
mp, err := conf.Parsed.ToPrefs()
|
mp, err := conf.Parsed.ToPrefs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -927,6 +930,7 @@ var assumeNetworkUpdateForTest = envknob.RegisterBool("TS_ASSUME_NETWORK_UP_FOR_
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) pauseOrResumeControlClientLocked() {
|
func (b *LocalBackend) pauseOrResumeControlClientLocked() {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
if b.cc == nil {
|
if b.cc == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1204,6 +1208,7 @@ func (b *LocalBackend) Prefs() ipn.PrefsView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) sanitizedPrefsLocked() ipn.PrefsView {
|
func (b *LocalBackend) sanitizedPrefsLocked() ipn.PrefsView {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
return stripKeysFromPrefs(b.pm.CurrentPrefs())
|
return stripKeysFromPrefs(b.pm.CurrentPrefs())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1335,6 +1340,7 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
|
func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
cn := b.currentNode()
|
cn := b.currentNode()
|
||||||
nm := cn.NetMap()
|
nm := cn.NetMap()
|
||||||
if nm == nil {
|
if nm == nil {
|
||||||
@ -1873,6 +1879,8 @@ func (b *LocalBackend) applySysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
|
|||||||
if !buildfeatures.HasSystemPolicy {
|
if !buildfeatures.HasSystemPolicy {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
|
|
||||||
if controlURL, err := b.polc.GetString(pkey.ControlURL, prefs.ControlURL); err == nil && prefs.ControlURL != controlURL {
|
if controlURL, err := b.polc.GetString(pkey.ControlURL, prefs.ControlURL); err == nil && prefs.ControlURL != controlURL {
|
||||||
prefs.ControlURL = controlURL
|
prefs.ControlURL = controlURL
|
||||||
anyChange = true
|
anyChange = true
|
||||||
@ -1941,6 +1949,8 @@ func (b *LocalBackend) applyExitNodeSysPolicyLocked(prefs *ipn.Prefs) (anyChange
|
|||||||
if !buildfeatures.HasUseExitNode {
|
if !buildfeatures.HasUseExitNode {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
|
|
||||||
if exitNodeIDStr, _ := b.polc.GetString(pkey.ExitNodeID, ""); exitNodeIDStr != "" {
|
if exitNodeIDStr, _ := b.polc.GetString(pkey.ExitNodeID, ""); exitNodeIDStr != "" {
|
||||||
exitNodeID := tailcfg.StableNodeID(exitNodeIDStr)
|
exitNodeID := tailcfg.StableNodeID(exitNodeIDStr)
|
||||||
|
|
||||||
@ -2182,6 +2192,8 @@ func (b *LocalBackend) resolveAutoExitNodeLocked(prefs *ipn.Prefs) (prefsChanged
|
|||||||
if !buildfeatures.HasUseExitNode {
|
if !buildfeatures.HasUseExitNode {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
|
|
||||||
// As of 2025-07-08, the only supported auto exit node expression is [ipn.AnyExitNode].
|
// As of 2025-07-08, the only supported auto exit node expression is [ipn.AnyExitNode].
|
||||||
//
|
//
|
||||||
// However, to maintain forward compatibility with future auto exit node expressions,
|
// However, to maintain forward compatibility with future auto exit node expressions,
|
||||||
@ -2295,6 +2307,8 @@ func (b *LocalBackend) setWgengineStatus(s *wgengine.Status, err error) {
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) setWgengineStatusLocked(s *wgengine.Status) {
|
func (b *LocalBackend) setWgengineStatusLocked(s *wgengine.Status) {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
|
|
||||||
es := b.parseWgStatusLocked(s)
|
es := b.parseWgStatusLocked(s)
|
||||||
cc := b.cc
|
cc := b.cc
|
||||||
|
|
||||||
@ -4312,6 +4326,7 @@ func (b *LocalBackend) EditPrefsAs(mp *ipn.MaskedPrefs, actor ipnauth.Actor) (ip
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) checkEditPrefsAccessLocked(actor ipnauth.Actor, prefs ipn.PrefsView, mp *ipn.MaskedPrefs) error {
|
func (b *LocalBackend) checkEditPrefsAccessLocked(actor ipnauth.Actor, prefs ipn.PrefsView, mp *ipn.MaskedPrefs) error {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
if mp.RunSSHSet && mp.RunSSH && !envknob.CanSSHD() {
|
if mp.RunSSHSet && mp.RunSSH && !envknob.CanSSHD() {
|
||||||
@ -4362,6 +4377,7 @@ func (b *LocalBackend) checkEditPrefsAccessLocked(actor ipnauth.Actor, prefs ipn
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) changeDisablesExitNodeLocked(prefs ipn.PrefsView, change *ipn.MaskedPrefs) bool {
|
func (b *LocalBackend) changeDisablesExitNodeLocked(prefs ipn.PrefsView, change *ipn.MaskedPrefs) bool {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
if !buildfeatures.HasUseExitNode {
|
if !buildfeatures.HasUseExitNode {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -4403,6 +4419,7 @@ func (b *LocalBackend) changeDisablesExitNodeLocked(prefs ipn.PrefsView, change
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) adjustEditPrefsLocked(prefs ipn.PrefsView, mp *ipn.MaskedPrefs) {
|
func (b *LocalBackend) adjustEditPrefsLocked(prefs ipn.PrefsView, mp *ipn.MaskedPrefs) {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
// Zeroing the ExitNodeID via localAPI must also zero the prior exit node.
|
// Zeroing the ExitNodeID via localAPI must also zero the prior exit node.
|
||||||
if mp.ExitNodeIDSet && mp.ExitNodeID == "" && !mp.InternalExitNodePriorSet {
|
if mp.ExitNodeIDSet && mp.ExitNodeID == "" && !mp.InternalExitNodePriorSet {
|
||||||
mp.InternalExitNodePrior = ""
|
mp.InternalExitNodePrior = ""
|
||||||
@ -4480,6 +4497,7 @@ func (b *LocalBackend) onEditPrefsLocked(_ ipnauth.Actor, mp *ipn.MaskedPrefs, o
|
|||||||
// startReconnectTimerLocked sets a timer to automatically set WantRunning to true
|
// startReconnectTimerLocked sets a timer to automatically set WantRunning to true
|
||||||
// after the specified duration.
|
// after the specified duration.
|
||||||
func (b *LocalBackend) startReconnectTimerLocked(d time.Duration) {
|
func (b *LocalBackend) startReconnectTimerLocked(d time.Duration) {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
if b.reconnectTimer != nil {
|
if b.reconnectTimer != nil {
|
||||||
// Stop may return false if the timer has already fired,
|
// Stop may return false if the timer has already fired,
|
||||||
// and the function has been called in its own goroutine,
|
// and the function has been called in its own goroutine,
|
||||||
@ -4522,11 +4540,13 @@ func (b *LocalBackend) startReconnectTimerLocked(d time.Duration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) resetAlwaysOnOverrideLocked() {
|
func (b *LocalBackend) resetAlwaysOnOverrideLocked() {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
b.overrideAlwaysOn = false
|
b.overrideAlwaysOn = false
|
||||||
b.stopReconnectTimerLocked()
|
b.stopReconnectTimerLocked()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) stopReconnectTimerLocked() {
|
func (b *LocalBackend) stopReconnectTimerLocked() {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
if b.reconnectTimer != nil {
|
if b.reconnectTimer != nil {
|
||||||
// Stop may return false if the timer has already fired,
|
// Stop may return false if the timer has already fired,
|
||||||
// and the function has been called in its own goroutine,
|
// and the function has been called in its own goroutine,
|
||||||
@ -4542,6 +4562,7 @@ func (b *LocalBackend) stopReconnectTimerLocked() {
|
|||||||
|
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) editPrefsLocked(actor ipnauth.Actor, mp *ipn.MaskedPrefs) (ipn.PrefsView, error) {
|
func (b *LocalBackend) editPrefsLocked(actor ipnauth.Actor, mp *ipn.MaskedPrefs) (ipn.PrefsView, error) {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
p0 := b.pm.CurrentPrefs()
|
p0 := b.pm.CurrentPrefs()
|
||||||
|
|
||||||
// Check if the changes in mp are allowed.
|
// Check if the changes in mp are allowed.
|
||||||
@ -5660,6 +5681,7 @@ func (b *LocalBackend) enterStateLocked(newState ipn.State) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *LocalBackend) hasNodeKeyLocked() bool {
|
func (b *LocalBackend) hasNodeKeyLocked() bool {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
// we can't use b.Prefs(), because it strips the keys, oops!
|
// we can't use b.Prefs(), because it strips the keys, oops!
|
||||||
p := b.pm.CurrentPrefs()
|
p := b.pm.CurrentPrefs()
|
||||||
return p.Valid() && p.Persist().Valid() && !p.Persist().PrivateNodeKey().IsZero()
|
return p.Valid() && p.Persist().Valid() && !p.Persist().PrivateNodeKey().IsZero()
|
||||||
@ -5680,9 +5702,11 @@ func (b *LocalBackend) NodeKey() key.NodePublic {
|
|||||||
//
|
//
|
||||||
// b.mu must be held
|
// b.mu must be held
|
||||||
func (b *LocalBackend) nextStateLocked() ipn.State {
|
func (b *LocalBackend) nextStateLocked() ipn.State {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
if b.health.IsUnhealthy(ipn.StateStoreHealth) {
|
if b.health.IsUnhealthy(ipn.StateStoreHealth) {
|
||||||
return ipn.NoState
|
return ipn.NoState
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cc = b.cc
|
cc = b.cc
|
||||||
cn = b.currentNode()
|
cn = b.currentNode()
|
||||||
@ -5758,6 +5782,8 @@ func (b *LocalBackend) nextStateLocked() ipn.State {
|
|||||||
//
|
//
|
||||||
// requires b.mu to be held.
|
// requires b.mu to be held.
|
||||||
func (b *LocalBackend) stateMachineLocked() {
|
func (b *LocalBackend) stateMachineLocked() {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
|
|
||||||
b.enterStateLocked(b.nextStateLocked())
|
b.enterStateLocked(b.nextStateLocked())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5767,6 +5793,7 @@ func (b *LocalBackend) stateMachineLocked() {
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) stopEngineAndWaitLocked() {
|
func (b *LocalBackend) stopEngineAndWaitLocked() {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
b.logf("stopEngineAndWait...")
|
b.logf("stopEngineAndWait...")
|
||||||
st, _ := b.e.ResetAndStop() // TODO: what should we do if this returns an error?
|
st, _ := b.e.ResetAndStop() // TODO: what should we do if this returns an error?
|
||||||
b.setWgengineStatusLocked(st)
|
b.setWgengineStatusLocked(st)
|
||||||
@ -5787,6 +5814,7 @@ func (b *LocalBackend) setControlClientLocked(cc controlclient.Client) {
|
|||||||
// returned value is non-nil, the caller must call Shutdown on it after
|
// returned value is non-nil, the caller must call Shutdown on it after
|
||||||
// releasing b.mu.
|
// releasing b.mu.
|
||||||
func (b *LocalBackend) resetControlClientLocked() controlclient.Client {
|
func (b *LocalBackend) resetControlClientLocked() controlclient.Client {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
if b.cc == nil {
|
if b.cc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -5813,6 +5841,8 @@ func (b *LocalBackend) resetControlClientLocked() controlclient.Client {
|
|||||||
|
|
||||||
// resetAuthURLLocked resets authURL, canceling any pending interactive login.
|
// resetAuthURLLocked resets authURL, canceling any pending interactive login.
|
||||||
func (b *LocalBackend) resetAuthURLLocked() {
|
func (b *LocalBackend) resetAuthURLLocked() {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
|
|
||||||
b.authURL = ""
|
b.authURL = ""
|
||||||
b.authURLTime = time.Time{}
|
b.authURLTime = time.Time{}
|
||||||
b.authActor = nil
|
b.authActor = nil
|
||||||
@ -5842,6 +5872,8 @@ func (b *LocalBackend) ShouldExposeRemoteWebClient() bool {
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) setWebClientAtomicBoolLocked(nm *netmap.NetworkMap) {
|
func (b *LocalBackend) setWebClientAtomicBoolLocked(nm *netmap.NetworkMap) {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
|
|
||||||
shouldRun := !nm.HasCap(tailcfg.NodeAttrDisableWebClient)
|
shouldRun := !nm.HasCap(tailcfg.NodeAttrDisableWebClient)
|
||||||
wasRunning := b.webClientAtomicBool.Swap(shouldRun)
|
wasRunning := b.webClientAtomicBool.Swap(shouldRun)
|
||||||
if wasRunning && !shouldRun {
|
if wasRunning && !shouldRun {
|
||||||
@ -5854,6 +5886,8 @@ func (b *LocalBackend) setWebClientAtomicBoolLocked(nm *netmap.NetworkMap) {
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) setExposeRemoteWebClientAtomicBoolLocked(prefs ipn.PrefsView) {
|
func (b *LocalBackend) setExposeRemoteWebClientAtomicBoolLocked(prefs ipn.PrefsView) {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
|
|
||||||
if !buildfeatures.HasWebClient {
|
if !buildfeatures.HasWebClient {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -5982,6 +6016,8 @@ func (b *LocalBackend) RefreshExitNode() {
|
|||||||
|
|
||||||
// refreshExitNodeLocked is like RefreshExitNode but requires b.mu be held.
|
// refreshExitNodeLocked is like RefreshExitNode but requires b.mu be held.
|
||||||
func (b *LocalBackend) refreshExitNodeLocked() {
|
func (b *LocalBackend) refreshExitNodeLocked() {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
|
|
||||||
if b.resolveExitNodeLocked() {
|
if b.resolveExitNodeLocked() {
|
||||||
b.authReconfigLocked()
|
b.authReconfigLocked()
|
||||||
}
|
}
|
||||||
@ -5997,6 +6033,8 @@ func (b *LocalBackend) refreshExitNodeLocked() {
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) resolveExitNodeLocked() (changed bool) {
|
func (b *LocalBackend) resolveExitNodeLocked() (changed bool) {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
|
|
||||||
if !buildfeatures.HasUseExitNode {
|
if !buildfeatures.HasUseExitNode {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -6058,6 +6096,7 @@ func (b *LocalBackend) reconcilePrefsLocked(prefs *ipn.Prefs) (changed bool) {
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) resolveExitNodeInPrefsLocked(prefs *ipn.Prefs) (changed bool) {
|
func (b *LocalBackend) resolveExitNodeInPrefsLocked(prefs *ipn.Prefs) (changed bool) {
|
||||||
|
syncs.RequiresMutex(&b.mu)
|
||||||
if !buildfeatures.HasUseExitNode {
|
if !buildfeatures.HasUseExitNode {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,3 +16,8 @@ type Mutex = sync.Mutex
|
|||||||
//
|
//
|
||||||
// It's only not a sync.RWMutex when built with the ts_mutex_debug build tag.
|
// It's only not a sync.RWMutex when built with the ts_mutex_debug build tag.
|
||||||
type RWMutex = sync.RWMutex
|
type RWMutex = sync.RWMutex
|
||||||
|
|
||||||
|
// RequiresMutex declares the caller assumes it has the given
|
||||||
|
// mutex held. In non-debug builds, it's a no-op and compiles to
|
||||||
|
// nothing.
|
||||||
|
func RequiresMutex(mu *sync.Mutex) {}
|
||||||
|
|||||||
@ -15,4 +15,8 @@ type RWMutex struct {
|
|||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RequiresMutex(mu *sync.Mutex) {
|
||||||
|
// TODO: check
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(bradfitz): actually track stuff when in debug mode.
|
// TODO(bradfitz): actually track stuff when in debug mode.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user