fix(chain): Copy returns wrong pointer, connection leaks in Connect/Handshake, deferred cancel accumulation in retry loops

- transport.go: Copy() returned original pointer instead of the allocated copy,
  causing data races and incorrect routing for multiplexed chains.
- route.go: Connect and Handshake failures in intermediate nodes leaked the
  returned connection. Handshake fix preserves the original Connect result
  (guaranteed non-nil) and closes both the Handshake return value and input.
- router.go: defer cancel() inside retry loops accumulated context timers
  across iterations. Wrapped each loop body in an IIFE so defer scopes
  per-iteration and timers are released immediately.
This commit is contained in:
ginuerzh
2026-05-23 16:30:53 +08:00
parent d015378654
commit 569572d6f7
3 changed files with 97 additions and 78 deletions
+10 -1
View File
@@ -271,20 +271,29 @@ func (r *chainRoute) connect(ctx context.Context, logger logger.Logger) (conn ne
} }
cc, err = preNode.Options().Transport.Connect(ctx, cn, "tcp", addr) cc, err = preNode.Options().Transport.Connect(ctx, cn, "tcp", addr)
if err != nil { if err != nil {
if cc != nil {
cc.Close()
}
cn.Close() cn.Close()
if marker != nil { if marker != nil {
marker.Mark() marker.Mark()
} }
return return
} }
cc, err = node.Options().Transport.Handshake(ctx, cc) var cc2 net.Conn
cc2, err = node.Options().Transport.Handshake(ctx, cc)
if err != nil { if err != nil {
if cc2 != nil {
cc2.Close()
}
cc.Close()
cn.Close() cn.Close()
if marker != nil { if marker != nil {
marker.Mark() marker.Mark()
} }
return return
} }
cc = cc2
if marker != nil { if marker != nil {
marker.Reset() marker.Reset()
} }
+13 -3
View File
@@ -90,6 +90,7 @@ func (r *Router) dial(ctx context.Context, network, address string, log logger.L
log.Debugf("dial %s/%s", address, network) log.Debugf("dial %s/%s", address, network)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
func() {
ctx := ctx ctx := ctx
if r.options.Timeout > 0 { if r.options.Timeout > 0 {
var cancel context.CancelFunc var cancel context.CancelFunc
@@ -106,7 +107,7 @@ func (r *Router) dial(ctx context.Context, network, address string, log logger.L
ipAddr, err = xnet.Resolve(ctx, "ip", address, r.options.Resolver, r.options.HostMapper, log) ipAddr, err = xnet.Resolve(ctx, "ip", address, r.options.Resolver, r.options.HostMapper, log)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
break return
} }
if buf != nil { if buf != nil {
@@ -139,9 +140,13 @@ func (r *Router) dial(ctx context.Context, network, address string, log logger.L
}, callerOpts...)..., }, callerOpts...)...,
) )
if err == nil { if err == nil {
break return
} }
log.Errorf("route(retry=%d) %s", i, err) log.Errorf("route(retry=%d) %s", i, err)
}()
if conn != nil || err == nil {
break
}
} }
return return
@@ -160,6 +165,7 @@ func (r *Router) Bind(ctx context.Context, network, address string, opts ...chai
log.Debugf("bind on %s/%s", address, network) log.Debugf("bind on %s/%s", address, network)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
func() {
ctx := ctx ctx := ctx
if r.options.Timeout > 0 { if r.options.Timeout > 0 {
var cancel context.CancelFunc var cancel context.CancelFunc
@@ -190,9 +196,13 @@ func (r *Router) Bind(ctx context.Context, network, address string, opts ...chai
} }
ln, err = route.Bind(ctx, network, address, opts...) ln, err = route.Bind(ctx, network, address, opts...)
if err == nil { if err == nil {
break return
} }
log.Errorf("route(retry=%d) %s", i, err) log.Errorf("route(retry=%d) %s", i, err)
}()
if ln != nil || err == nil {
break
}
} }
return return
+1 -1
View File
@@ -102,5 +102,5 @@ func (tr *Transport) Options() *chain.TransportOptions {
func (tr *Transport) Copy() chain.Transporter { func (tr *Transport) Copy() chain.Transporter {
tr2 := &Transport{} tr2 := &Transport{}
*tr2 = *tr *tr2 = *tr
return tr return tr2
} }