From 26f46ffff82af4bf32c21ce19c7e159cfa18f0c3 Mon Sep 17 00:00:00 2001 From: cppla Date: Fri, 9 Oct 2026 19:44:01 +0800 Subject: [PATCH] Fix H3 migration metadata race in maintained QUIC dependency --- THIRD_PARTY_NOTICES.md | 2 +- docs/DEPENDENCY-MAINTENANCE.md | 9 +++++- docs/releases/v1.2.0.md | 6 ++++ go.mod | 2 +- go.sum | 4 +-- internal/tunnel/web_udp_test.go | 42 ++++++++++++++++++++++++---- scripts/check-dependency-boundary.sh | 2 +- 7 files changed, 56 insertions(+), 11 deletions(-) diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 344482d..459a814 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -8,7 +8,7 @@ The SHA-256 value is calculated from the upstream file's original bytes; line en ## `github.com/apernet/quic-go` `v0.63.1-0.20261004180939-a10df75c260c` -Effective source replacement: `github.com/cppla/quic-go v0.63.1-0.20261009040133-c1cae948af15`. +Effective source replacement: `github.com/cppla/quic-go v0.63.1-0.20261009113701-3a0e2cdb87a8`. ### `LICENSE` diff --git a/docs/DEPENDENCY-MAINTENANCE.md b/docs/DEPENDENCY-MAINTENANCE.md index e50a17e..81978b3 100644 --- a/docs/DEPENDENCY-MAINTENANCE.md +++ b/docs/DEPENDENCY-MAINTENANCE.md @@ -66,7 +66,14 @@ The fork adds explicit opt-in browser-profile session resumption, its regressions, and narrowly scoped test/build maintenance. It also fixes CRYPTO tail offset and memory ownership across later handshake flights, including cold HelloRetryRequest handshakes, without changing the initial cold packet -layout. See [the exact patch queue](https://github.com/cppla/quic-go/blob/main/FORK.md). +layout. The migration follow-up synchronizes active send-connection replacement +with public address accessors, remote-address updates and connection-state +reads. Gated publication tests and real concurrent path migration reproduce +the old race; AutoCAR additionally checks that authentication established by +HTTP/3 CONNECT-UDP survives migration for a subsequent TCP stream on the same +physical connection. This does not enable +automatic client path switching in AutoCAR. +See [the exact patch queue](https://github.com/cppla/quic-go/blob/main/FORK.md). Preserve the existing full-handshake profile, certificate verification, transport-parameter ownership and per-client cache isolation. A resumed TLS connection still requires fresh AutoCAR authentication; diff --git a/docs/releases/v1.2.0.md b/docs/releases/v1.2.0.md index dc00b35..6edc7f4 100644 --- a/docs/releases/v1.2.0.md +++ b/docs/releases/v1.2.0.md @@ -14,6 +14,8 @@ TLS 会话恢复,并修复连接限额、失败请求清理和 HTTP 响应尾 `chrome-133`。该选项也用于 `web-auto` 的 H2 回退路径。 - 改善 H2 TLS 会话恢复与 HelloRetryRequest 兼容性,避免失败响应清理拖延拨号 返回,同时保留共享连接上其他流的可用性。 +- 修复 Web H3 依赖在主动路径迁移时与地址、连接状态读取之间的数据竞争, + 保留迁移后同一物理连接的认证状态;不新增自动切换客户端网络路径的策略。 - 修复 native QUIC/TLS 全局连接限额共享,约束未认证连接及空闲 QUIC 会话; 保留 HTTP 响应中正文结束时才产生的正常 trailers,并过滤禁止字段和 `Connection` 指定的逐跳字段。正文截断仍作为失败处理。 @@ -56,6 +58,10 @@ TLS 会话恢复,并修复连接限额、失败请求清理和 HTTP 响应尾 - 开发阶段一次 macOS 全量 race 运行出现原有 UDP 测试的物理 QUIC 拨号超时, 原因尚未确定;该用例后续 20 次独立 race 运行及原顺序全量重放均通过。 未跳过测试或放宽断言,记录见 [PR #81](https://github.com/cppla/autocar/pull/81)。 +- 发布前 main 的 race 门禁发现 H3 主动路径迁移竞争,已在旧依赖上复现并 + 修复共享状态同步,增加并发读取与真实迁移回归;原失败记录保留于 + [CI 37923588436](https://github.com/cppla/autocar/actions/runs/37923588436)。 + 发布须以修复后同一 main 提交重新通过全部门禁,不能沿用旧提交的通过结果。 - 发布包覆盖 Linux amd64/arm64、macOS arm64、Windows amd64,内含可执行文件、 许可证与第三方许可清单,并提供 `SHA256SUMS`。交叉构建和元数据检查不等于 每种平台均完成原生部署验收,仍需按实际网络、证书与应用验证。 diff --git a/go.mod b/go.mod index aa46bc7..ca53685 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ go 1.27.2 replace github.com/refraction-networking/utls => github.com/cppla/utls v0.0.0-20261009031926-14c2a4cb1403 // The web adapter is independent of native transport's official QUIC module. -replace github.com/apernet/quic-go => github.com/cppla/quic-go v0.63.1-0.20261009040133-c1cae948af15 +replace github.com/apernet/quic-go => github.com/cppla/quic-go v0.63.1-0.20261009113701-3a0e2cdb87a8 require ( github.com/apernet/quic-go v0.63.1-0.20261004180939-a10df75c260c diff --git a/go.sum b/go.sum index 03d56af..d47be7c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/cppla/quic-go v0.63.1-0.20261009040133-c1cae948af15 h1:pymPD+qKo2BoQchlq1Ke70OXRAWPfJp25Jjj7+1+F8A= -github.com/cppla/quic-go v0.63.1-0.20261009040133-c1cae948af15/go.mod h1:TqjSZptjiHgfRItER3Mn5ymcv8OBcpfZkC2W9qn4WI0= +github.com/cppla/quic-go v0.63.1-0.20261009113701-3a0e2cdb87a8 h1:cRdU8MvvT8WQsIrEEW7Iu2yRkWwzKc0qILo7yLSqSeA= +github.com/cppla/quic-go v0.63.1-0.20261009113701-3a0e2cdb87a8/go.mod h1:TqjSZptjiHgfRItER3Mn5ymcv8OBcpfZkC2W9qn4WI0= github.com/cppla/utls v0.0.0-20261009031926-14c2a4cb1403 h1:4mCd3Kcelm0jFNEWI//rA80LenP2OiMc4E4it17nLQU= github.com/cppla/utls v0.0.0-20261009031926-14c2a4cb1403/go.mod h1:rc/ctWeKlh9LSk5k90KlSPcwF7P6D31mn/9GI2NAfBI= github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ= diff --git a/internal/tunnel/web_udp_test.go b/internal/tunnel/web_udp_test.go index e89b05b..a837531 100644 --- a/internal/tunnel/web_udp_test.go +++ b/internal/tunnel/web_udp_test.go @@ -9,6 +9,7 @@ import ( "net" "net/http" "net/netip" + "runtime" "strconv" "sync" "sync/atomic" @@ -292,11 +293,9 @@ func TestWebH3ConnectionAuthSurvivesClientPathMigration(t *testing.T) { if physicalConnection == nil || session == nil || session.authState != webH3ClientAuthReady { t.Fatal("CONNECT-UDP did not establish connection authentication before migration") } - // Wait until the HTTP/3 control stream has consumed SETTINGS before asking - // quic-go to switch paths. The migration API mutates the active path, while - // control-stream initialization reads ConnectionState; overlapping those two - // operations exercises an upstream initialization race instead of the - // connection-authentication behavior this test is meant to cover. + // Finish HTTP/3 setup before exercising authenticated path migration. + // Metadata reads below deliberately overlap the path change: neither this + // SETTINGS barrier nor Path.Switch is a substitute for dependency locking. settingsContext, cancelSettings := context.WithTimeout(context.Background(), 2*time.Second) err = waitWebH3DatagramSettings(settingsContext, session.client) cancelSettings() @@ -317,6 +316,34 @@ func TestWebH3ConnectionAuthSurvivesClientPathMigration(t *testing.T) { _ = migratedTransport.Close() _ = migratedSocket.Close() }) + metadataContext, stopMetadata := context.WithCancel(context.Background()) + metadataStarted, metadataDone := make(chan struct{}), make(chan struct{}) + go func() { + defer close(metadataDone) + close(metadataStarted) + for { + select { + case <-metadataContext.Done(): + return + default: + } + // These exported methods are also used by normal stream creation + // and HTTP/3. Reading them during migration must remain race-free. + _ = physicalConnection.LocalAddr().String() + _ = physicalConnection.RemoteAddr().String() + _ = physicalConnection.ConnectionState() + runtime.Gosched() + } + }() + t.Cleanup(func() { + stopMetadata() + select { + case <-metadataDone: + case <-time.After(2 * time.Second): + t.Error("connection metadata reader did not stop") + } + }) + <-metadataStarted path, err := physicalConnection.AddPath(migratedTransport) if err != nil { t.Fatal(err) @@ -330,6 +357,11 @@ func TestWebH3ConnectionAuthSurvivesClientPathMigration(t *testing.T) { if err := path.Switch(); err != nil { t.Fatal(err) } + // Switch requests migration asynchronously; keep concurrent metadata reads + // active until the run loop has actually selected the probed path. + waitForCondition(t, 2*time.Second, func() bool { + return physicalConnection.LocalAddr().String() == migratedSocket.LocalAddr().String() + }, "authenticated connection to select the migrated path") if err := exchange(client, tcpTarget, "after-migration"); err != nil { t.Fatal(err) diff --git a/scripts/check-dependency-boundary.sh b/scripts/check-dependency-boundary.sh index 3c73461..4b7fb85 100755 --- a/scripts/check-dependency-boundary.sh +++ b/scripts/check-dependency-boundary.sh @@ -4,7 +4,7 @@ set -Eeuo pipefail readonly ALLOWED_WEB_QUIC_MODULE='github.com/apernet/quic-go' readonly ALLOWED_WEB_QUIC_VERSION='v0.63.1-0.20261004180939-a10df75c260c' readonly ALLOWED_WEB_QUIC_REPLACEMENT='github.com/cppla/quic-go' -readonly ALLOWED_WEB_QUIC_FORK_VERSION='v0.63.1-0.20261009040133-c1cae948af15' +readonly ALLOWED_WEB_QUIC_FORK_VERSION='v0.63.1-0.20261009113701-3a0e2cdb87a8' readonly ALLOWED_UTLS_MODULE='github.com/refraction-networking/utls' readonly ALLOWED_UTLS_UPSTREAM_VERSION='v1.8.3-0.20261006222701-ff1b50fbbe9a' readonly ALLOWED_UTLS_REPLACEMENT='github.com/cppla/utls'