Fix HTTP/2 stream errors leaking into nested readers and transport data races

This commit is contained in:
世界
2026-09-20 22:24:38 +08:00
parent 9be5fcf4e1
commit 18b0317018
5 changed files with 39 additions and 17 deletions
+2 -2
View File
@@ -45,9 +45,9 @@ require (
github.com/sagernet/netlink v0.0.0-20260814022025-64455d367bbf
github.com/sagernet/nftables v0.3.0-mod.4
github.com/sagernet/quic-go v0.61.0-sing-box-mod.7
github.com/sagernet/sing v0.9.4
github.com/sagernet/sing v0.9.5
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3
github.com/sagernet/sing-mux v0.3.6
github.com/sagernet/sing-mux v0.3.8
github.com/sagernet/sing-openconnect v0.0.0-20260810065514-53aa8058f8df
github.com/sagernet/sing-openvpn v0.0.0-20260729104525-103eb5fe5eb6
github.com/sagernet/sing-quic v0.7.0
+4 -4
View File
@@ -318,12 +318,12 @@ github.com/sagernet/nftables v0.3.0-mod.4 h1:vnOtcDYeSXv2e5RoRuGH0lrpttQFJ8iC4IC
github.com/sagernet/nftables v0.3.0-mod.4/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ=
github.com/sagernet/quic-go v0.61.0-sing-box-mod.7 h1:sW2O+DoNF+my1PGlSZ3zG2N+fdxLR3DC0l4sXCM0lBE=
github.com/sagernet/quic-go v0.61.0-sing-box-mod.7/go.mod h1:hmLC8GJPp+BrpBgrgJnacvI4fZGIhasuy1ROJJ8GR4E=
github.com/sagernet/sing v0.9.4 h1:nIdH+Yvy4NzTezB+c3z/9of1jLRggnrtMtb3uMOIaNM=
github.com/sagernet/sing v0.9.4/go.mod h1:K3Owt3xPhHugvlnlPPxZJ/exXdaJfEPOTNorGk4AXjo=
github.com/sagernet/sing v0.9.5 h1:BHaElRqhHhkH747xIkxgsqgRJxkIiuAlpnfwiJnu2aY=
github.com/sagernet/sing v0.9.5/go.mod h1:K3Owt3xPhHugvlnlPPxZJ/exXdaJfEPOTNorGk4AXjo=
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3 h1:3y6++yIa8XlDhxPkpR4p+7RUHVY2KTP9CPIGnWmOlO8=
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3/go.mod h1:XEqEDYRCAYLaoPjZ1ifVWJg5iWAJHL2gOAXe/PM28Cg=
github.com/sagernet/sing-mux v0.3.6 h1:ix+CPhZsR9XIAmEVLIUdz+itDzXs1T6iKO8eF/XOVOQ=
github.com/sagernet/sing-mux v0.3.6/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
github.com/sagernet/sing-mux v0.3.8 h1:mM0TLUkr36aEhgUHi9Qh60qcAbD2fZcmnJ30937xxrY=
github.com/sagernet/sing-mux v0.3.8/go.mod h1:jricpwCOndDsTEM1TM3piJvhL/2Mm7OhLkm+JadJJ1Q=
github.com/sagernet/sing-openconnect v0.0.0-20260810065514-53aa8058f8df h1:ogyC7KPTWueN7kXFhKSdHeS5/t/zBjQgAOpWjqnrMos=
github.com/sagernet/sing-openconnect v0.0.0-20260810065514-53aa8058f8df/go.mod h1:4AKZLVcvY3r54UaK2Gbnm7aN8pOwdLz+y4EP0QFZ5Eg=
github.com/sagernet/sing-openvpn v0.0.0-20260729104525-103eb5fe5eb6 h1:ZAvhkor0prJ8KEX9EmTEH+gJ1WlA3ffjS8GZT6KKq9c=
+12 -2
View File
@@ -64,7 +64,7 @@ func (c *GunConn) Read(b []byte) (n int, err error) {
}
func (c *GunConn) read(b []byte) (n int, err error) {
if c.reader == nil {
if c.create != nil {
<-c.create
if c.err != nil {
return 0, c.err
@@ -144,7 +144,17 @@ func (c *GunConn) FrontHeadroom() int {
}
func (c *GunConn) Close() error {
err := common.Close(c.rawReader, c.writer)
var reader io.Reader
if c.create != nil {
select {
case <-c.create:
reader = c.rawReader
default:
}
} else {
reader = c.rawReader
}
err := common.Close(reader, c.writer)
if c.cancel != nil {
c.cancel()
}
+8 -6
View File
@@ -16,6 +16,7 @@ import (
"github.com/sagernet/sing-box/common/tls"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -30,7 +31,7 @@ type Client struct {
ctx context.Context
dialer N.Dialer
serverAddr M.Socksaddr
transport http.RoundTripper
transport common.TypedValue[http.RoundTripper]
http2 bool
requestURL url.URL
host []string
@@ -77,7 +78,7 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
if !strings.HasPrefix(requestURL.Path, "/") {
requestURL.Path = "/" + requestURL.Path
}
return &Client{
client := &Client{
ctx: ctx,
dialer: dialer,
serverAddr: serverAddr,
@@ -85,9 +86,10 @@ func NewClient(ctx context.Context, dialer N.Dialer, serverAddr M.Socksaddr, opt
host: options.Host,
method: options.Method,
headers: options.Headers.Build(),
transport: transport,
http2: tlsConfig != nil,
}, nil
}
client.transport.Store(transport)
return client, nil
}
func (c *Client) DialContext(ctx context.Context) (net.Conn, error) {
@@ -151,7 +153,7 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
cancel()
})
go func() {
response, err := c.transport.RoundTrip(request)
response, err := c.transport.Load().RoundTrip(request)
handshakeTimer.Stop()
if err != nil {
if handshakeTimedOut.Load() {
@@ -169,6 +171,6 @@ func (c *Client) dialHTTP2(ctx context.Context) (net.Conn, error) {
}
func (c *Client) Close() error {
c.transport = ResetTransport(c.transport)
c.transport.Store(ResetTransport(c.transport.Load()))
return nil
}
+13 -3
View File
@@ -159,10 +159,10 @@ func (c *HTTP2Conn) Setup(reader io.Reader, err error) {
}
func (c *HTTP2Conn) Read(b []byte) (n int, err error) {
if c.reader == nil {
if c.create != nil {
<-c.create
if c.err != nil {
return 0, c.err
return 0, baderror.WrapH2(c.err)
}
}
n, err = c.reader.Read(b)
@@ -175,7 +175,17 @@ func (c *HTTP2Conn) Write(b []byte) (n int, err error) {
}
func (c *HTTP2Conn) Close() error {
err := common.Close(c.reader, c.writer)
var reader io.Reader
if c.create != nil {
select {
case <-c.create:
reader = c.reader
default:
}
} else {
reader = c.reader
}
err := common.Close(reader, c.writer)
if c.cancel != nil {
c.cancel()
}