PQ Crypta Logo

Where HTTP/3 Clients Fail

20 failures across 11 tests, grouped by what was being tested rather than by who failed it.

PQ CRYPTA PLATFORM

🏠 Main

🧪 Interactive Apps

📰 News

🛡️ PQ Crypta Proxy

👤 Account

⟨ QUANTUM ERROR PORTAL ⟩

Navigate the Error Dimensions

Grouped by test, not by client, because that is what makes the interesting case visible: a clause several independent implementations get wrong the same way is not several bugs. Four teams reading one sentence and arriving at one wrong answer is evidence about the sentence.

Every entry below is a MUST or a SHOULD the client did not meet, with the clause it was judged against. Discretionary tests cannot appear here: where the RFC permits either behaviour, the report says which was chosen and grades nothing. Nor can inconclusive runs — a test the client was never actually put through is not a failure of the client.

These are specification violations, not vulnerabilities. Some have security relevance and most are simply behaviour a document prohibits; calling every one a vulnerability is how a conformance suite loses the right to be believed about the ones that are.

PUSH_PROMISE for a push the client never allowed

h-push-promise-unsolicited · RFC 9114 §7.2.5, §4.6 · correctness · MUST · 5 of 7 implementations

5 independent implementations fail this one. Worth reading as a question about the clause before reading it as a question about the clients.

Close the connection with H3_ID_ERROR. §7.2.7 leaves the maximum push ID unset until the client sends MAX_PUSH_ID, so a server "cannot push until it receives a MAX_PUSH_ID frame" and every push ID is larger than the client has advertised. §7.2.5 is explicit about the answer: a client "MUST treat receipt of a PUSH_PROMISE frame that contains a larger push ID than the client has advertised as a connection error of H3_ID_ERROR".

ImplementationVersionWhat it did
quinn noq fork Fail Rejected, but with error 0x105 where the specification requires 0x108. The violation was detected; the code reported is wrong.
aioquic 1.3.0 Fail Rejected, but with error 0x105 where the specification requires 0x108. The violation was detected; the code reported is wrong.
chromium build 1223 Fail Rejected, but with error 0x106 where the specification requires 0x108. The violation was detected; the code reported is wrong.
quiche 0.30.0 Fail Rejected, but with error 0x105 where the specification requires 0x108. The violation was detected; the code reported is wrong.
neqo unknown Fail Rejected, but with error 0x105 where the specification requires 0x108. The violation was detected; the code reported is wrong.

Transport parameter carrying a value the specification forbids

q-invalid-transport-param · RFC 9000 §7.4, §18.2 · correctness · MUST · 3 of 7 implementations

3 independent implementations fail this one. Worth reading as a question about the clause before reading it as a question about the clients.

Close the connection with TRANSPORT_PARAMETER_ERROR. §18.2 makes an ack_delay_exponent above 20 invalid, and §7.4 is a MUST: "An endpoint MUST treat receipt of a transport parameter with an invalid value as a connection error of type TRANSPORT_PARAMETER_ERROR." Distinct from a *duplicate* parameter, which the same clause makes only a SHOULD — this port sends one parameter, once, with a value outside its permitted range. The parameter travels in the handshake, so every client that connects here reads it. Only a client that answers with a CONNECTION_CLOSE this endpoint can read is scored: one that simply abandons the handshake is recorded as inconclusive, because a close that was sent and lost looks exactly like one that was never sent.

ImplementationVersionWhat it did
chromium build 1223 Fail rejected the out-of-range parameter with PROTOCOL_VIOLATION, where RFC 9000 §7.4 requires TRANSPORT_PARAMETER_ERROR. The violation was detected; the code reported is wrong.
quic-go quic-go v0.61.0 Fail rejected the out-of-range parameter with INTERNAL_ERROR, where RFC 9000 §7.4 requires TRANSPORT_PARAMETER_ERROR. The violation was detected; the code reported is wrong.
neqo unknown Fail rejected the out-of-range parameter with Code::crypto(2f), where RFC 9000 §7.4 requires TRANSPORT_PARAMETER_ERROR. The violation was detected; the code reported is wrong.

103 Early Hints before the final response

h-early-hints · RFC 9110 §15.2, RFC 8297 · resilience · MUST · 2 of 7 implementations

2 independent implementations fail this one. Worth reading as a question about the clause before reading it as a question about the clients.

Treat 103 as informational and keep reading for the final response. RFC 9110 §15.2 makes this a MUST: a client "MUST be able to parse one or more 1xx responses received prior to a final response, even if the client does not expect one". Ignoring the hints is fine — a user agent MAY do that — but closing the connection is not.

ImplementationVersionWhat it did
quinn noq fork Fail Gave up with error 0x105 instead of recovering.
aioquic 1.3.0 Fail Gave up with error 0x10e instead of recovering.

CANCEL_PUSH for a push ID that was never promised

h-cancel-push-unsolicited · RFC 9114 §7.2.3 · correctness · MUST · 2 of 7 implementations

2 independent implementations fail this one. Worth reading as a question about the clause before reading it as a question about the clients.

Close the connection with H3_ID_ERROR. No MAX_PUSH_ID was granted, so every push ID is greater than currently allowed, and §7.2.3 requires a CANCEL_PUSH referencing one to be treated as a connection error of that type.

ImplementationVersionWhat it did
quinn noq fork Fail Rejected, but with error 0x105 where the specification requires 0x108. The violation was detected; the code reported is wrong.
chromium build 1223 Fail Rejected, but with error 0x106 where the specification requires 0x108. The violation was detected; the code reported is wrong.

Push stream opened for a push nobody allowed

h-push-stream-unpromised · RFC 9114 §6.2.2 · correctness · MUST · 2 of 7 implementations

2 independent implementations fail this one. Worth reading as a question about the clause before reading it as a question about the clients.

Close the connection with H3_ID_ERROR. §6.2.2 does not require the push to have been promised first — the stream alone is enough: a client "MUST treat receipt of a push stream as a connection error of type H3_ID_ERROR when no MAX_PUSH_ID frame has been sent", and no client under test sends one. Distinct from the PUSH_PROMISE test, which puts the same violation in a frame on the control stream. This one opens the push stream itself, which a client has to recognise by its stream type before any frame inside it is read.

ImplementationVersionWhat it did
chromium build 1223 Fail Rejected, but with error 0x101 where the specification requires 0x108. The violation was detected; the code reported is wrong.
quiche 0.30.0 Fail Rejected, but with error 0x103 where the specification requires 0x108. The violation was detected; the code reported is wrong.

Unknown frame type in a 1-RTT packet

q-reserved-frame · RFC 9000 §12.4 · correctness · MUST · 1 of 7 implementations

Close the connection with FRAME_ENCODING_ERROR. Unlike HTTP/3, QUIC reserves no ignorable frame types — §12.4 makes an unknown frame a connection error, so ignoring it is the failure here.

ImplementationVersionWhat it did
neqo unknown Fail rejected the unknown frame type with INTERNAL_ERROR, where RFC 9000 §12.4 requires FRAME_ENCODING_ERROR. The violation was detected; the code reported is wrong.

Stateless reset

q-stateless-reset · RFC 9000 §10.3 · correctness · MUST · 1 of 7 implementations

Recognise the token in the last 16 bytes of the datagram, enter the draining period, and send no further packets on the connection. The packet cannot be authenticated, so there is nothing to reply with and nothing to report to the peer — continuing to send is the failure §10.3.1 names.

ImplementationVersionWhat it did
aioquic 1.3.0 Fail kept sending after the Stateless Reset: 76 more datagram(s) arrived in the following 3 seconds. RFC 9000 §10.3.1 requires a client that recognises the token to enter the draining period and send nothing further, so this connection is wedged against an endpoint that has forgotten it.

GOAWAY sent mid-connection

h-goaway · RFC 9114 §5.2 · resilience · SHOULD · 1 of 7 implementations

Stop opening requests, finish those in flight, and retry idempotent ones elsewhere.

ImplementationVersionWhat it did
chromium build 1223 Fail Dropped the connection instead of recovering.

DATA frame before any HEADERS on the response stream

h-data-before-headers · RFC 9114 §4.1 · correctness · MUST · 1 of 7 implementations

Close the connection with H3_FRAME_UNEXPECTED. A response begins with a field section, and §4.1 makes "receipt of an invalid sequence of frames" a connection error of that type — a body arriving before the headers that describe it is exactly that.

ImplementationVersionWhat it did
quiche 0.30.0 Fail Accepted a protocol violation and carried on. The anomaly was in the response the client read and delivered, so it was seen; this should have been rejected.

SETTINGS_H3_DATAGRAM with a value that is neither 0 nor 1

h-datagram-setting-invalid · RFC 9297 §2.1.1 · correctness · MUST · 1 of 7 implementations

Close the connection with H3_SETTINGS_ERROR. Unusually for a setting, RFC 9297 pins down the invalid-value case rather than leaving it open: the value "MUST be either 0 or 1", and if one "is received with a value that is neither 0 nor 1, the receiver MUST terminate the connection with error H3_SETTINGS_ERROR". This is the setting that gates HTTP Datagrams and so WebTransport, which means a client that implements either has real parsing behind it rather than an ignored identifier.

ImplementationVersionWhat it did
quic-go quic-go v0.61.0 Fail Rejected, but with error 0x106 where the specification requires 0x109. The violation was detected; the code reported is wrong.

A second GOAWAY naming a larger identifier than the first

h-goaway-increasing · RFC 9114 §5.2 · correctness · MUST · 1 of 7 implementations

Close the connection with H3_ID_ERROR. §5.2 permits multiple GOAWAY frames but requires the identifier in each to be no greater than any previously sent, because the identifier is a promise about what will still be processed and raising it takes that promise back. "Receiving a GOAWAY containing a larger identifier than previously received MUST be treated as a connection error of type H3_ID_ERROR."

ImplementationVersionWhat it did
neqo unknown Fail Rejected, but with error 0x101 where the specification requires 0x108. The violation was detected; the code reported is wrong.

What this suite is · The full grid · All clients · All tests · Findings