Browse Source

Fix race codes

世界 1 month ago
parent
commit
146383499e
9 changed files with 62 additions and 40 deletions
  1. 4 0
      Makefile
  2. 13 0
      common/tls/ech.go
  3. 2 2
      common/tls/ech_stub.go
  4. 1 6
      common/tls/std_server.go
  5. 4 2
      common/urltest/urltest.go
  6. 2 2
      go.mod
  7. 4 4
      go.sum
  8. 20 16
      route/rule/rule_set_local.go
  9. 12 8
      route/rule/rule_set_remote.go

+ 4 - 0
Makefile

@@ -17,6 +17,10 @@ build:
 	export GOTOOLCHAIN=local && \
 	go build $(MAIN_PARAMS) $(MAIN)
 
+race:
+	export GOTOOLCHAIN=local && \
+	go build -race $(MAIN_PARAMS) $(MAIN)
+
 ci_build:
 	export GOTOOLCHAIN=local && \
 	go build $(PARAMS) $(MAIN) && \

+ 13 - 0
common/tls/ech.go

@@ -81,6 +81,19 @@ func parseECHServerConfig(ctx context.Context, options option.InboundTLSOptions,
 	return nil
 }
 
+func (c *STDServerConfig) setECHServerConfig(echKey []byte) error {
+	echKeys, err := parseECHKeys(echKey)
+	if err != nil {
+		return err
+	}
+	c.access.Lock()
+	config := c.config.Clone()
+	config.EncryptedClientHelloKeys = echKeys
+	c.config = config
+	c.access.Unlock()
+	return nil
+}
+
 func parseECHKeys(echKey []byte) ([]tls.EncryptedClientHelloKey, error) {
 	block, _ := pem.Decode(echKey)
 	if block == nil || block.Type != "ECH KEYS" {

+ 2 - 2
common/tls/ech_stub.go

@@ -18,6 +18,6 @@ func parseECHServerConfig(ctx context.Context, options option.InboundTLSOptions,
 	return E.New("ECH requires go1.24, please recompile your binary.")
 }
 
-func reloadECHKeys(echKeyPath string, tlsConfig *tls.Config) error {
-	return E.New("ECH requires go1.24, please recompile your binary.")
+func (c *STDServerConfig) setECHServerConfig(echKey []byte) error {
+	panic("unreachable")
 }

+ 1 - 6
common/tls/std_server.go

@@ -163,15 +163,10 @@ func (c *STDServerConfig) certificateUpdated(path string) error {
 		if err != nil {
 			return E.Cause(err, "reload ECH keys from ", c.echKeyPath)
 		}
-		echKeys, err := parseECHKeys(echKey)
+		err = c.setECHServerConfig(echKey)
 		if err != nil {
 			return err
 		}
-		c.access.Lock()
-		config := c.config.Clone()
-		config.EncryptedClientHelloKeys = echKeys
-		c.config = config
-		c.access.Unlock()
 		c.logger.Info("reloaded ECH keys")
 	}
 	return nil

+ 4 - 2
common/urltest/urltest.go

@@ -47,15 +47,15 @@ func (s *HistoryStorage) LoadURLTestHistory(tag string) *adapter.URLTestHistory
 func (s *HistoryStorage) DeleteURLTestHistory(tag string) {
 	s.access.Lock()
 	delete(s.delayHistory, tag)
-	s.access.Unlock()
 	s.notifyUpdated()
+	s.access.Unlock()
 }
 
 func (s *HistoryStorage) StoreURLTestHistory(tag string, history *adapter.URLTestHistory) {
 	s.access.Lock()
 	s.delayHistory[tag] = history
-	s.access.Unlock()
 	s.notifyUpdated()
+	s.access.Unlock()
 }
 
 func (s *HistoryStorage) notifyUpdated() {
@@ -69,6 +69,8 @@ func (s *HistoryStorage) notifyUpdated() {
 }
 
 func (s *HistoryStorage) Close() error {
+	s.access.Lock()
+	defer s.access.Unlock()
 	s.updateHook = nil
 	return nil
 }

+ 2 - 2
go.mod

@@ -27,13 +27,13 @@ require (
 	github.com/sagernet/gomobile v0.1.8
 	github.com/sagernet/gvisor v0.0.0-20250325023245-7a9c0f5725fb
 	github.com/sagernet/quic-go v0.52.0-beta.1
-	github.com/sagernet/sing v0.7.8
+	github.com/sagernet/sing v0.7.10
 	github.com/sagernet/sing-mux v0.3.3
 	github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb
 	github.com/sagernet/sing-shadowsocks v0.2.8
 	github.com/sagernet/sing-shadowsocks2 v0.2.1
 	github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11
-	github.com/sagernet/sing-tun v0.7.1
+	github.com/sagernet/sing-tun v0.7.2
 	github.com/sagernet/sing-vmess v0.2.7
 	github.com/sagernet/smux v1.5.34-mod.2
 	github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.1

+ 4 - 4
go.sum

@@ -167,8 +167,8 @@ github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/l
 github.com/sagernet/quic-go v0.52.0-beta.1 h1:hWkojLg64zjV+MJOvJU/kOeWndm3tiEfBLx5foisszs=
 github.com/sagernet/quic-go v0.52.0-beta.1/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4=
 github.com/sagernet/sing v0.6.9/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
-github.com/sagernet/sing v0.7.8 h1:i3JBTzeEOqMRtYcyNV17LKvxkb3mr2Y/omM5ldvhCYo=
-github.com/sagernet/sing v0.7.8/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
+github.com/sagernet/sing v0.7.10 h1:2yPhZFx+EkyHPH8hXNezgyRSHyGY12CboId7CtwLROw=
+github.com/sagernet/sing v0.7.10/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
 github.com/sagernet/sing-mux v0.3.3 h1:YFgt9plMWzH994BMZLmyKL37PdIVaIilwP0Jg+EcLfw=
 github.com/sagernet/sing-mux v0.3.3/go.mod h1:pht8iFY4c9Xltj7rhVd208npkNaeCxzyXCgulDPLUDA=
 github.com/sagernet/sing-quic v0.5.2-0.20250909083218-00a55617c0fb h1:5Wx3XeTiKrrrcrAky7Hc1bO3CGxrvho2Vu5b/adlEIM=
@@ -179,8 +179,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnq
 github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
 github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w=
 github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
-github.com/sagernet/sing-tun v0.7.1 h1:bgoe9ixY9G+y8FN5y2szD/0Kr1wQqYuuiCzsXefNDBE=
-github.com/sagernet/sing-tun v0.7.1/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM=
+github.com/sagernet/sing-tun v0.7.2 h1:uJkAZM0KBqIYzrq077QGqdvj/+4i/pMOx6Pnx0jYqAs=
+github.com/sagernet/sing-tun v0.7.2/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM=
 github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiYj0sk=
 github.com/sagernet/sing-vmess v0.2.7/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs=
 github.com/sagernet/smux v1.5.34-mod.2 h1:gkmBjIjlJ2zQKpLigOkFur5kBKdV6bNRoFu2WkltRQ4=

+ 20 - 16
route/rule/rule_set_local.go

@@ -27,16 +27,16 @@ import (
 var _ adapter.RuleSet = (*LocalRuleSet)(nil)
 
 type LocalRuleSet struct {
-	ctx            context.Context
-	logger         logger.Logger
-	tag            string
-	rules          []adapter.HeadlessRule
-	metadata       adapter.RuleSetMetadata
-	fileFormat     string
-	watcher        *fswatch.Watcher
-	callbackAccess sync.Mutex
-	callbacks      list.List[adapter.RuleSetUpdateCallback]
-	refs           atomic.Int32
+	ctx        context.Context
+	logger     logger.Logger
+	tag        string
+	access     sync.RWMutex
+	rules      []adapter.HeadlessRule
+	metadata   adapter.RuleSetMetadata
+	fileFormat string
+	watcher    *fswatch.Watcher
+	callbacks  list.List[adapter.RuleSetUpdateCallback]
+	refs       atomic.Int32
 }
 
 func NewLocalRuleSet(ctx context.Context, logger logger.Logger, options option.RuleSet) (*LocalRuleSet, error) {
@@ -141,11 +141,11 @@ func (s *LocalRuleSet) reloadRules(headlessRules []option.HeadlessRule) error {
 	metadata.ContainsProcessRule = hasHeadlessRule(headlessRules, isProcessHeadlessRule)
 	metadata.ContainsWIFIRule = hasHeadlessRule(headlessRules, isWIFIHeadlessRule)
 	metadata.ContainsIPCIDRRule = hasHeadlessRule(headlessRules, isIPCIDRHeadlessRule)
+	s.access.Lock()
 	s.rules = rules
 	s.metadata = metadata
-	s.callbackAccess.Lock()
 	callbacks := s.callbacks.Array()
-	s.callbackAccess.Unlock()
+	s.access.Unlock()
 	for _, callback := range callbacks {
 		callback(s)
 	}
@@ -157,10 +157,14 @@ func (s *LocalRuleSet) PostStart() error {
 }
 
 func (s *LocalRuleSet) Metadata() adapter.RuleSetMetadata {
+	s.access.RLock()
+	defer s.access.RUnlock()
 	return s.metadata
 }
 
 func (s *LocalRuleSet) ExtractIPSet() []*netipx.IPSet {
+	s.access.RLock()
+	defer s.access.RUnlock()
 	return common.FlatMap(s.rules, extractIPSetFromRule)
 }
 
@@ -181,14 +185,14 @@ func (s *LocalRuleSet) Cleanup() {
 }
 
 func (s *LocalRuleSet) RegisterCallback(callback adapter.RuleSetUpdateCallback) *list.Element[adapter.RuleSetUpdateCallback] {
-	s.callbackAccess.Lock()
-	defer s.callbackAccess.Unlock()
+	s.access.Lock()
+	defer s.access.Unlock()
 	return s.callbacks.PushBack(callback)
 }
 
 func (s *LocalRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSetUpdateCallback]) {
-	s.callbackAccess.Lock()
-	defer s.callbackAccess.Unlock()
+	s.access.Lock()
+	defer s.access.Unlock()
 	s.callbacks.Remove(element)
 }
 

+ 12 - 8
route/rule/rule_set_remote.go

@@ -40,16 +40,16 @@ type RemoteRuleSet struct {
 	logger         logger.ContextLogger
 	outbound       adapter.OutboundManager
 	options        option.RuleSet
-	metadata       adapter.RuleSetMetadata
 	updateInterval time.Duration
 	dialer         N.Dialer
+	access         sync.RWMutex
 	rules          []adapter.HeadlessRule
+	metadata       adapter.RuleSetMetadata
 	lastUpdated    time.Time
 	lastEtag       string
 	updateTicker   *time.Ticker
 	cacheFile      adapter.CacheFile
 	pauseManager   pause.Manager
-	callbackAccess sync.Mutex
 	callbacks      list.List[adapter.RuleSetUpdateCallback]
 	refs           atomic.Int32
 }
@@ -120,10 +120,14 @@ func (s *RemoteRuleSet) PostStart() error {
 }
 
 func (s *RemoteRuleSet) Metadata() adapter.RuleSetMetadata {
+	s.access.RLock()
+	defer s.access.RUnlock()
 	return s.metadata
 }
 
 func (s *RemoteRuleSet) ExtractIPSet() []*netipx.IPSet {
+	s.access.RLock()
+	defer s.access.RUnlock()
 	return common.FlatMap(s.rules, extractIPSetFromRule)
 }
 
@@ -144,14 +148,14 @@ func (s *RemoteRuleSet) Cleanup() {
 }
 
 func (s *RemoteRuleSet) RegisterCallback(callback adapter.RuleSetUpdateCallback) *list.Element[adapter.RuleSetUpdateCallback] {
-	s.callbackAccess.Lock()
-	defer s.callbackAccess.Unlock()
+	s.access.Lock()
+	defer s.access.Unlock()
 	return s.callbacks.PushBack(callback)
 }
 
 func (s *RemoteRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSetUpdateCallback]) {
-	s.callbackAccess.Lock()
-	defer s.callbackAccess.Unlock()
+	s.access.Lock()
+	defer s.access.Unlock()
 	s.callbacks.Remove(element)
 }
 
@@ -185,13 +189,13 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
 			return E.Cause(err, "parse rule_set.rules.[", i, "]")
 		}
 	}
+	s.access.Lock()
 	s.metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
 	s.metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
 	s.metadata.ContainsIPCIDRRule = hasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
 	s.rules = rules
-	s.callbackAccess.Lock()
 	callbacks := s.callbacks.Array()
-	s.callbackAccess.Unlock()
+	s.access.Unlock()
 	for _, callback := range callbacks {
 		callback(s)
 	}