google / google/quiche

External client can trigger quic_bug_10586_3 in quiche based servers

Open
#147 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
897
Forks
177
PR merge metrics
No merged PRs in 30d

Description

I was currently testing my quiche based node.js webtransport plugin, against node.js quic implementation.

During the test I saw: F0517 05:33:10.985518 17324 quic_stream.cc:818] quic_bug_10586_3: Fin already buffered, or RESET_STREAM_AT sent

I hunted down the cause and I can mitigate my own code. But the pattern is probably an issue also for other quiche-based implementations,

It works as follows: 1.) quiche is running the server 2.) node.js is the quic is the client.

3.) I during my frist test I passed aheader when creating the bidrectional stream for the session, which automatically sends a fin in the control stream and so WebTransportHttp3::OnConnectStreamFinReceived is called. This immediately sends a fin on the control stream.
4.) The parsing of the initial body is inflight and does not know about fin, so that calls to size_t QuicSpdyStream::WriteHeaders or similar calls of QuicSpdyStream cause quic_bug_10586_3.

My lib is derived partially from libquiche test, though the issue is present in: https://github.com/google/quiche/blob/ce53d3090da59a80e2f08c39a5fefe3b6db7d44f/quiche/quic/tools/quic_simple_server_stream.cc#L164 https://github.com/google/quiche/blob/ce53d3090da59a80e2f08c39a5fefe3b6db7d44f/quiche/quic/tools/quic_simple_server_stream.cc#L417 etc. and may be in other quiche based code. (I do not know if envoy may be affected).

Please look at my repo at the commit: https://github.com/fails-components/webtransport/commit/ccc15b57c0e8413f8bb91d3c3f3f9ca8d7bfff5c it mitigates the issue, and I would suggest that the examples in the quiche repo are updated.

Steps to reproduce: 1.) Build current (as of mid may, but should still work) node.js main (I did it on windows and use the PR quic-issue-63216 https://github.com/nodejs/node/pull/63230, but I do not think it matters) 2.) Connect to a webtransport endpoint and path on a quiche based server, e.g. with something like:

import { connect } from 'node:quic'
import { isIP } from 'node:net'

function(host, port, path) {
if (typeof port === 'undefined') port = 443
/** @type {import('../session.js').HttpClient} */

const quicOptions = {
alpn: 'h3', // it is the default,
application: {
enableConnectProtocol: true, // needed to start a webtransport session
enableDatagrams: true,
maxStreamWindow: 6 * 1024 * 1024,
maxWindow: 15 * 1024 * 1024
// I am wondering, how certificates are verified.
},
transportParams: {
initialMaxStreamDataBidiLocal: 100,
initialMaxStreamDataBidiRemote: 100,
initialMaxStreamDataUni: 100,
initialMaxData: 15 * 1024 * 1024, // or something else
initialMaxStreamsBidi: 100,
initialMaxStreamsUni: 100,
maxDatagramFrameSize: 1200 // required to start a webtransport session
}
}
if (!isIP(host)) {
quicOptions.servername = host
}

const clientInt = connect(host + ':' + port, quicOptions)
console.log('tried to connect to', host + ':' + port)
let connected = false
clientInt
.then(async (session) => {
console.log('http3 client created')

session.onsessionticket = (ticket) => {
console.log('ticket', ticket)
}
await session.opened
console.log('http3 client connected')

let headersReceivedResolve
const headersReceivedProm = new Promise((resolve, reject) => {
headersReceivedResolve = resolve
})

session
.createBidirectionalStream({
headers: { /* this is important as it immediately sends a fin */
':method': 'CONNECT',
':scheme': 'https',
// this one depends on draft, draft14 says "webtransport", draft15 says "webtransport-h3"
':protocol': 'webtransport',
':path': path,
':authority': host + ':' + port
},
onheaders(headers) {
headersReceivedResolve(headers)
}
})
.then((stream) => {
console.log('session established')
})
.catch((error) => {
console.log('Error creating sessionstream')
throw error
})
})
.catch((error) => {
connected = false
console.log('http3 connection fail', error)
})
}

Note that this is untested code I distilled from my code inside a bigger framework, though you should be able to build code to trigger the event. The path should point to a webtransport endpoint.

If this is applied to an implementation that did not mitigate the issue, you can trigger the quic bug and crash the server. (Note I have this issue before using another way, and the team said I should file it here so that it may be fixed.)

Contributor guide

Open the contributing guide

Research direction

Start with quiche/quic/tools/quic_simple_server_stream.cc at the referenced lines 164 and 417, then trace WebTransportHttp3::OnConnectStreamFinReceived and QuicSpdyStream::WriteHeaders. Reproduce the WebTransport connection using the supplied Node.js example and verify that the server no longer crashes with quic_bug_10586_3; update the quiche examples if appropriate.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, javascript, node.js
Domain
networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.