go.mod pins the QUIC stack to a fork based on quic-go v0.45:
require github.com/quic-go/quic-go v0.52.0
replace github.com/quic-go/quic-go => github.com/chungthuang/quic-go v0.45.1-0.20250428085412-43229ad201fd
The replace has no version on its left side, so it wins and the require line above it does nothing. That shadows every quic-go release since v0.45, including the fix for CVE-2025-59530 (7.5), which is patched in 0.49.1, 0.54.1 and 0.55.0. The pinned base is older than all three, so as far as I can tell no released cloudflared carries the fix.
It's a client-side crash. A server that sends HANDSHAKE_DONE before the handshake actually finishes makes the client drop Handshake keys before Initial keys, and the next undecryptable packet then trips the "no packets are queued after completion of the handshake" assert. cloudflared is the client here, so it takes something acting as the edge to hit it, but it's still a remote panic in the connection path.
I see the v0.59.1 bump landed twice and was reverted twice, both times shortly before a release:
68620ef landed 2026-06-12, reverted by 2bcaf09 on 2026-06-18 13:30. 2026.6.1 went out at 14:40.
02eb75b landed 2026-06-18 18:20, reverted by a53d9e5 on 2026-07-08 10:38. 2026.7.0 went out at 13:10.
Neither revert records a reason, so I won't guess at one. From outside the only visible thing is that v0.59 is a big move: the tracing and metrics code under quic/ is written against logging.PacketType and quic.Connection, and both moved.
If the blocker is the size of that move rather than the fix itself, the fix backports onto the current pin by itself. handleHandshakeConfirmed in the v0.45 fork is the same as today's upstream apart from the receiver name and the time type, so quic-go#5354 is seven lines:
diff --git a/connection.go b/connection.go
--- a/connection.go
+++ b/connection.go
@@ -851,6 +851,13 @@ func (s *connection) handleHandshakeComplete(now time.Time) error {
}
func (s *connection) handleHandshakeConfirmed(now time.Time) error {
+ // Drop initial keys.
+ // On the client side, this should have happened when sending the first Handshake packet,
+ // but this is not guaranteed if the server misbehaves.
+ // See CVE-2025-59530 for more details.
+ if err := s.dropEncryptionLevel(protocol.EncryptionInitial, now); err != nil {
+ return err
+ }
if err := s.dropEncryptionLevel(protocol.EncryptionHandshake, now); err != nil {
return err
}
Dropping Initial keys twice is safe on this base, which matters because the normal client path already dropped them by that point. sentPacketHandler.DropPackets returns early when the packet number space is already gone, DiscardInitialKeys is nil-safe, and cryptoStream.Finish only errors when the crypto stream still has unread data. Upstream relies on the same property, its own test asserts DiscardInitialKeys is called twice, and upstream's dropEncryptionLevel has no guard the v0.45 fork lacks.
I applied it to the pinned revision and built and tested a project that embeds the tunnel transport in-process. Build and the tunnel transport tests stay green. I could not run quic-go's own suite against it, a vendored copy has no test files, so that needs a run inside the fork.
I can open the PR against chungthuang/quic-go so the pin can move to a new revision of the same base, or you can just take the diff, it needs no attribution. This is meant as an interim step and the v0.59 upgrade still works whenever it's convenient, at which point this can be dropped.
go.mod pins the QUIC stack to a fork based on quic-go v0.45:
The replace has no version on its left side, so it wins and the require line above it does nothing. That shadows every quic-go release since v0.45, including the fix for CVE-2025-59530 (7.5), which is patched in 0.49.1, 0.54.1 and 0.55.0. The pinned base is older than all three, so as far as I can tell no released cloudflared carries the fix.
It's a client-side crash. A server that sends HANDSHAKE_DONE before the handshake actually finishes makes the client drop Handshake keys before Initial keys, and the next undecryptable packet then trips the "no packets are queued after completion of the handshake" assert. cloudflared is the client here, so it takes something acting as the edge to hit it, but it's still a remote panic in the connection path.
I see the v0.59.1 bump landed twice and was reverted twice, both times shortly before a release:
68620eflanded 2026-06-12, reverted by2bcaf09on 2026-06-18 13:30. 2026.6.1 went out at 14:40.02eb75blanded 2026-06-18 18:20, reverted bya53d9e5on 2026-07-08 10:38. 2026.7.0 went out at 13:10.Neither revert records a reason, so I won't guess at one. From outside the only visible thing is that v0.59 is a big move: the tracing and metrics code under
quic/is written againstlogging.PacketTypeandquic.Connection, and both moved.If the blocker is the size of that move rather than the fix itself, the fix backports onto the current pin by itself.
handleHandshakeConfirmedin the v0.45 fork is the same as today's upstream apart from the receiver name and the time type, so quic-go#5354 is seven lines:Dropping Initial keys twice is safe on this base, which matters because the normal client path already dropped them by that point.
sentPacketHandler.DropPacketsreturns early when the packet number space is already gone,DiscardInitialKeysis nil-safe, andcryptoStream.Finishonly errors when the crypto stream still has unread data. Upstream relies on the same property, its own test assertsDiscardInitialKeysis called twice, and upstream'sdropEncryptionLevelhas no guard the v0.45 fork lacks.I applied it to the pinned revision and built and tested a project that embeds the tunnel transport in-process. Build and the tunnel transport tests stay green. I could not run quic-go's own suite against it, a vendored copy has no test files, so that needs a run inside the fork.
I can open the PR against chungthuang/quic-go so the pin can move to a new revision of the same base, or you can just take the diff, it needs no attribution. This is meant as an interim step and the v0.59 upgrade still works whenever it's convenient, at which point this can be dropped.