Bugs we found upstream

What our conformance suite, name servers and own operations found in other projects' software, where we reported it, and where each fix is now

10reports sent
5projects
3fixed upstream
0in a release
1awaiting a reply
13found, not yet reported
xquic: Reported lsquic: Acknowledged Knot DNS: Under consideration Knot DNS: Not reproduced yet msquic: Fix proposed Knot DNS: Partly fixed Chromium: Fixed, not yet released Knot DNS: Fixed, not yet released Knot DNS: Declined
Reported 1 Acknowledged 1 Under consideration 1 Not reproduced yet 1 Fix proposed 1 Partly fixed 1 Fixed, not yet released 2 Declined 1 and 1 sent privately, described here once fixed

Status from the projects' own trackers (GitHub, Chromium's Gerrit and Chromium Dash, CZ.NIC's GitLab, Launchpad), checked 40 minutes ago. Found by the HTTP/3 conformance suite (4) and our name servers (5).

Chromium · HTTP/3 · RFC 9114 §4.1

HTTP/3 responses with trailers never finish in Chromium

Fixed, not yet released First in Chromium 160.0.8158.0In Canary; Stable is 158.0.8127.37

Chromium decodes the response headers, the DATA frame and the trailing HEADERS frame of an HTTP/3 response, then fails the body read with net::ERR_CONNECTION_CLOSED (-100). The request fails, and a navigation to the response never reaches load. The same response without trailers loads normally.

It fails with and without content-length, with and without a Trailer header field, and against two unrelated servers (aioquic 1.7.2 and a quinn-based one). Nothing on the wire is malformed: RFC 9114 §4.1 allows a trailing field section after the content, Chromium sends no STOP_SENDING, RESET_STREAM or CONNECTION_CLOSE over it, and the connection stays usable.

What the net log shows

One failing request, trimmed. The whole response (headers, body, trailers and FIN) arrives in one STREAM frame, is decoded, and the fourth body read fails:

HTTP3_HEADERS_DECODED      {"headers": [":status: 200", "content-type: text/plain", "trailer: x-trailer"], "stream_id": 8}
HTTP3_DATA_FRAME_RECEIVED  {"payload_length": 26, "stream_id": 8}
HTTP3_HEADERS_DECODED      {"headers": ["x-trailer: after-the-body"], "stream_id": 8}
HTTP_TRANSACTION_READ_BODY {}
HTTP_TRANSACTION_READ_BODY {}
HTTP_TRANSACTION_READ_BODY {}
HTTP_TRANSACTION_READ_BODY {"net_error": -100}
FAILED                     {"net_error": -100}

Why the wire could not show it

Our suite judges a client by what the server observes, and fails a client only on positive evidence. Chromium's failure leaves none: no STOP_SENDING, no RESET_STREAM, no CONNECTION_CLOSE, and the connection goes on serving other requests. An earlier version of the trailers test scored that as a pass, because nothing showed on the wire; the current one records it as inconclusive, because a client that decodes trailers completes its request and this one reported failure.

In the matrix today, Chromium on h-trailers is inconclusive: Nothing on the wire objected to the response, and the client process then reported the request as failed (exit status 1). A client that decodes this completes its request, so the silence is no evidence it did: whether it refused the response without signalling or failed for a reason of its own is not shown.

So we read the client's own account. Chromium's net log showed the response decoded and the body read failing with -100, and a short aioquic server reproduced it with nothing of ours involved: /plain without trailers loads in 43 ms, while /, /nt (no Trailer field) and /cl (with content-length) never finish.

The cause

Chromium's engineers traced it within a week; this is their analysis, from the issue:

  1. Once trailers have been decoded, QUICHE's IsDoneReading() stays false until the trailers are marked consumed, even after the body and the FIN have been read.
  2. QuicHttpStream posts a task to read the trailers and completes the headers at once. The first ReadResponseBody() reads the 26-byte body; the second finds no bytes and the stream not done, so it parks a callback and returns ERR_IO_PENDING.
  3. The posted task consumes the trailers, which makes the stream done, and OnReadTrailingHeadersComplete() then closes the read side with OnFinRead(). With the request's FIN already sent, that closes the stream, and the close resolves the parked body read with ERR_CONNECTION_CLOSED instead of end of body.

The OnFinRead() call is a holdover from before an earlier refactoring (crbug.com/716563), when a stream's close still resolved a pending callback with OK. The unit test for trailers never caught it because it lets the event loop run before the first body read, so the trailers are consumed while body bytes are still unread. In the words of the analysis: a latent issue that has been around since at least 2021.

Who it affects

Any HTTP/3 response that ends with a trailer section (gRPC over HTTP/3, a Server-Timing or digest sent after the body), received by anything built on Chromium's network stack:

Browsers on other network stacks are not built on this code: Firefox uses neqo, and Safari Apple's own stack. Every other client in our matrix that completes the trailers test is listed below.

Where the fix is

The fix, “Fix ERR_CONNECTION_CLOSED on HTTP/3 responses with trailers”, defers closing the read side while a body read is pending and makes every consumption of trailers notify a pending read of end of body. It landed on 12 Nov 2026 and is first in Chromium 160.0.8158.0.

Canary 160.0.8164.0: carries the fix Canary 160.0.8164.0 Dev 160.0.8157.0: before the fix Dev 160.0.8157.0 Beta 159.0.8154.6: before the fix Beta 159.0.8154.6 Stable 158.0.8127.37: before the fix Stable 158.0.8127.37
Chrome's release channels, current version in each (Windows), from Chromium Dash: filled where the version carries the fix.

Reported 4 Nov 2026 as Issue 581692068, with the net log and the aioquic reproduction. Found with Chrome 156.0.8078.4 (Chromium, headless, through Playwright), Linux x86-64.

Trailers across HTTP/3 clients, measured

ClientVersionVerdictWhat the server saw
ngtcp2 + nghttp3 libcurl/8.23.0 ngtcp2/1.26.0 pass Decoded it and completed the request.
quinn + h3 (our fork) (ours) noq fork (2d81eb2) pass Decoded it and completed the request.
aioquic 1.7.2 (PyPI wheel) pass Decoded it and completed the request.
Chromium QUICHE build 1254 inconclusive Nothing on the wire objected to the response, and the client process then reported the request as failed (exit status 1). A client that decodes this completes its request, so the silence is no evidence it did: whether it refused the response without signalling or failed for a reason of its own is not shown.
quic-go quic-go v0.65.0 (h1:RAro2j2yN9a9) pass Decoded it and completed the request.
Cloudflare quiche 0.30.0 (d7387df) pass Decoded it and completed the request.
neqo (Firefox) 0.34.0 (7279e4b) pass Decoded it and completed the request.
picoquic + picotls 3a0c3bc pass Decoded it and completed the request.
ngtcp2 + nghttp3 (direct) e82176c pass Decoded it and completed the request.
LiteSpeed lsquic + BoringSSL 4.11.1 (cd23e52) fail Rejected the response with error 0x10e. Nothing here violates the specification — this is valid HTTP/3 that a client is required to be able to process.
Alibaba xquic + BoringSSL 8099bd2 + demo_client IPv6 fix pass Decoded it and completed the request.
.NET HttpClient + msquic 2.7.0 (d3d28f7) pass Decoded it and completed the request.
quico + lemon-tls 0.8.0 (npm) pass Decoded it and completed the request.

Source: the conformance matrix of 15 Nov 2026, test h-trailers.

Every report we sent

Grouped by project, newest first. Each says what found it, what we sent, and where it stands now; a report answered by email carries the maintainers' answer and its date.

Chromium

msquic

Transport error codes (src/core/frame.h) · RFC 9000 §20.1, §10.2.3

QUIC_ERROR_APPLICATION_ERROR is 0xB, but RFC 9000 assigns 0x0b to INVALID_TOKEN (APPLICATION_ERROR is 0x0c)

Fix proposed Pull request #6491 open: Fix QUIC_ERROR_APPLICATION_ERROR to use the RFC 9000 value 0xC

The constant is used in both directions. Sending: an application close that has to go in an Initial or Handshake packet becomes a transport CONNECTION_CLOSE carrying 0x0b, so the peer is told its token was invalid. Receiving: the check that waits for the 1-RTT application close fires on a peer's INVALID_TOKEN and misses a real APPLICATION_ERROR.

The suite met it on its post-quantum certificate test: .NET aborted the handshake itself before the TLS alert, msquic sent that close as 0xB, and the server read it as an invalid token.

Found by the HTTP/3 conformance suite in msquic main at d3d28f7 (v2.7.0), through .NET HttpClient. Sent 4 Nov 2026 on GitHub: microsoft/msquic#6456.

lsquic

Client, packet resizing after a Retry · RFC 9000 §17.2.5

Client aborts with an internal error after a Retry: split_lost_packet rejects a resize into one packet

Acknowledged A maintainer replied on 2026-11-06: "Thanks for the bug report! This is similar to #505."

About one connection in 13, the client aborts with "Internal error" immediately after a valid Retry and sends nothing more, so the server never sees the Initial carrying the token. It happens exactly when lsquic picked an 8-byte original DCID, which made our Retry token 69 bytes: the two Initial packets of a post-quantum first flight have to be rebuilt around the token, and the resize refuses a split into one packet (unexpected values resizing lost packet: count: 1).

Found by the HTTP/3 conformance suite in lsquic master cd23e52 (4.11.1), BoringSSL. Sent 4 Nov 2026 on GitHub: litespeedtech/lsquic#726.

xquic

Connection close (src/transport/xqc_packet_out.c) · RFC 9000 §10.2.3, RFC 9001 §4.9.2

CONNECTION_CLOSE before handshake confirmation is sent in a Handshake packet only, not also in 1-RTT

Reported No reply on the issue yet

A client that closes after completing the handshake but before it is confirmed sends its CONNECTION_CLOSE in a Handshake packet and nowhere else. A server that has confirmed the handshake has already discarded its Handshake keys, so to the server the client simply went silent, and the HTTP/3 error code is lost because the Handshake packet carries the transport form of the close.

Found by the HTTP/3 conformance suite in xquic main at 8099bd2, BoringSSL. Sent 4 Nov 2026 on GitHub: alibaba/xquic#1031.

Knot DNS

DNS over QUIC (src/libknot/quic/quic.c) · RFC 9000 §7.3, §8.1.3

A NEW_TOKEN token is taken for a Retry token, and the connection fails CID authentication

Partly fixed On masterLatest release v3.6.2 (2026-10-21) does not include it

A client presenting a token it received in a NEW_TOKEN frame was treated as one answering a Retry: the server sent a retry_source_connection_id and left original_destination_connection_id empty, and a client that checks both (RFC 9000 §7.3) closed the connection. Every resumed connection from a client that keeps NEW_TOKEN tokens failed this way.

CZ.NIC committed this one in our name. Tested 2026-11-15: Knot 3.6.2 with that commit still fails a NEW_TOKEN reconnect, closing with TRANSPORT_PARAMETER_ERROR ("original_destination_connection_id does not match"): the commit takes the first of the patch's two changes, and the regular-token path still leaves the original DCID empty.

Commit 41e5c93b “libknot/QUIC: correct handling of non-retry token in Initial pkt”, 29 Oct 2026

Found by our name servers in Knot DNS 3.6.1, embedded ngtcp2. Sent 21 Oct 2026 by email to CZ.NIC (Report 3 of 5).

Outgoing requests over QUIC (src/knot/query/requestor.c) · RFC 9250 §4.2.1

Knot's own requests over QUIC carry a random message ID, where RFC 9250 requires 0

Fixed, not yet released On masterLatest release v3.6.2 (2026-10-21) does not include it

SOA refreshes, zone transfers and NOTIFYs sent over QUIC used the random ID Knot generates for UDP and TCP. RFC 9250 §4.2.1 requires message ID 0 over QUIC (the stream pairs a query with its answer), and a server that enforces it closes the connection, failing the refresh, transfer or NOTIFY.

CZ.NIC committed this one in our name.

Commit 90207e0a “requestor/QUIC: query msgID must be zero (RFC 9250)”, 29 Oct 2026

Found by our name servers in Knot DNS 3.6.1. Sent 21 Oct 2026 by email to CZ.NIC (Report 5 of 5).

DNS over QUIC (src/knot/server/handler.c) · RFC 9250 §4.2, §4.2.1, §4.3.3, §5.5.2

RFC 9250 §4.3.3 protocol errors are answered, or dropped, instead of closing the connection

Under consideration

A query with a message ID other than 0 is answered, a second query on the same stream is dropped with the connection kept, and a query carrying the edns-tcp-keepalive option is answered. RFC 9250 §4.3.3 makes each a protocol error that closes the connection with DOQ_PROTOCOL_ERROR; only DOQ_EXCESSIVE_LOAD is ever sent.

CZ.NIC, 31 Oct 2026: Closing with the proper reason is what the RFCs require and helps debugging, but ignoring the errors does not look dangerous, spares resources and may shrink the attack surface; they need more time to find the best solution.

Found by our name servers in Knot DNS 3.6.1. Sent 21 Oct 2026 by email to CZ.NIC (Report 4 of 5).

Outgoing QUIC requests

Concurrent outgoing QUIC requests from one knotd fail with "connection reset"

Not reproduced yet

When one knotd makes several QUIC requests to the same remote at once (refreshes of several zones from one primary, NOTIFYs of several zones to one secondary), a large share fail with "connection reset": 9 of 20 refreshes with the default background workers, 0 of 20 with one. With QUIC debug logging on, the failures stop.

CZ.NIC, 31 Oct 2026: Not reproduced yet; they will keep trying.

What we run until then: background-workers: 1 on every knotd that makes outgoing QUIC requests.

Found by our name servers in Knot DNS 3.6.1, GnuTLS 3.8.13. Sent 21 Oct 2026 by email to CZ.NIC (Report 2 of 5).

TLS and QUIC early data (src/libknot/quic/tls_common.c) · RFC 8446 §8, RFC 9250 §4.5, §7.1

0-RTT: the anti-replay store records nothing, no Too Early for non-replayable opcodes, no setting to turn early data off

Declined

GnuTLS's ClientHello recording calls a function Knot supplies to store each ClientHello, and that function stores nothing, so within GnuTLS's freshness window the same early data is accepted each time it arrives. An UPDATE received in early data is refused without the Too Early error RFC 9250 §4.5 provides, and there is no setting to turn early data off.

CZ.NIC, 31 Oct 2026: Not a security issue in their view: within DNS, replaying a query or response within a limited window does little harm, and QUERY, NOTIFY and UPDATE are the only opcodes implemented. They added Extended DNS Error "Too Early" to the refusal of an UPDATE in early data.

What we run until then: Early data is off on our name servers.

Found by our name servers in Knot DNS 3.6.1, GnuTLS 3.8.13. Sent 21 Oct 2026 by email to CZ.NIC, as security-relevant (Report 1 of 5).

Found here, reported by others first

uutils coreutils · install, as Ubuntu 26.04 ships it (rust-coreutils 0.10.0)

install /dev/stdin TARGET fails with "No such file or directory" when TARGET already exists

Fixed, not yet released Ubuntu: TriagedLatest release 0.12.0 does not include it

Found when a deploy script that writes files to other hosts with install -m MODE /dev/stdin TARGET failed on the two hosts running Ubuntu 26.04. uutils follows /dev/stdin to /proc/self/fd/0, which names a pipe rather than a file, and only when the target exists. Already reported by others; we confirmed the fix and changed our script.

Tracked at Launchpad #2146464, uutils/coreutils#12407; the fix is uutils/coreutils#15438. What we run: Write to a temporary file beside the target and rename it over the target.

Withdrawn, declined, and not sent

Chromium and a mid-connection GOAWAY: withdrawn, the fault was ours

A draft said Chromium dropped the connection on a GOAWAY instead of finishing the request in flight. It came from one run and did not reproduce in three re-runs. The suite had written GOAWAY at connection setup, where it raced the client's first request, and RFC 9114 §5.2 makes a client that reads GOAWAY before sending anything right to go elsewhere. Chromium did the correct thing. The suite now sends GOAWAY once the request is in flight, and nothing was filed.

quic-go and server push: not sent, already declined

quic-go completes a request past an unsolicited PUSH_PROMISE without objecting. Someone else had already reported that as quic-go#5686, and its maintainers closed it as not planned: quic-go does not support server push and will not add its validation. A report from us would have asked for work they had decided against.

Push IDs: a specification question, not seven bugs

Of the HTTP/3 clients that object to an unsolicited PUSH_PROMISE, every one sends a code other than the H3_ID_ERROR RFC 9114 requires, most of them H3_FRAME_UNEXPECTED: §7.2.5 describes the frame as unexpected in prose, while the code it requires is in §4.6 under push IDs. That is one ambiguity read the same way by many careful teams, better raised once with the working group than as an issue in each project. The measurements are on the findings page.

Found, not yet reported

Each is a defect we can show, most with the change we run in our own builds. Each goes upstream once a person has decided it is worth the maintainers' time, written to stand on its own: versions, what we saw, the clause, a reproduction and our change.

Beyond these, the conformance matrix records 86 failing verdicts across 11 clients, grouped by the clause each breaks on the findings page; this page lists the ones written up as reports.

How a finding becomes a report

Questions

Why does Chrome fail HTTP/3 responses that have trailers?

Chromium closed the read side of the stream when it finished consuming the trailers while a body read was still waiting, and the waiting read was answered with ERR_CONNECTION_CLOSED instead of the end of the body. Nothing on the wire is wrong: RFC 9114 §4.1 allows trailers after the body.

Which browsers does the Chromium HTTP/3 trailers bug affect?

Everything built on Chromium's network stack that receives an HTTP/3 response with trailers: Google Chrome, Microsoft Edge, Opera, Brave, Vivaldi, Samsung Internet and other Chromium-based browsers, Android System WebView, apps using Cronet, Electron and CEF desktop apps, and headless Chromium. Browsers on other network stacks, such as Firefox and Safari, are not built on this code.

Which Chrome version fixes HTTP/3 responses with trailers?

The fix landed in Chromium 160.0.8158.0. Chrome Stable was 158.0.8127.37 when this page last checked, so Stable does not carry it yet.

How does PQ Crypta find bugs in HTTP/3 clients?

Its conformance suite runs real HTTP/3 clients against a server that misbehaves on purpose, one anomaly per port, and judges each client by what the server observed on the wire, not by the client's exit status. A failure needs positive evidence; silence is recorded as inconclusive. A failure that survives repeated runs is written up with a reproduction, and a person decides whether it is worth a maintainer's time.

What did PQ Crypta report to Knot DNS?

Five defects found running Knot DNS 3.6.1 for our own zones with DNS over QUIC and zone transfers over QUIC: a NEW_TOKEN token taken for a Retry token, a random message ID on its own QUIC requests, RFC 9250 protocol errors not closing the connection, concurrent outgoing QUIC requests resetting, and an anti-replay store that records nothing. CZ.NIC committed two of the fixes in our name.

⟨ QUANTUM ERROR PORTAL ⟩

Navigate the Error Dimensions