Description
According to RFC 9000:
An endpoint will know the final size for a stream when the receiving part of the stream enters the "Size Known" or "Reset Recvd" state (Section 3). The receiver MUST use the final size of the stream to account for all bytes sent on the stream in its connection-level flow controller.
❌ Current MsQuic Behavior
In MsQuic, when a STREAM
frame with the FIN
flag is received before all stream data has arrived, the implementation:
- ✅ Correctly sets
Stream->RecvMaxLength = EndOffset
(i.e., final size) - ❌ But does not immediately charge the full final size to
Stream->Connection->Send.OrderedStreamBytesReceived
Instead, the code only credits newly written bytes to the flow control tracker :
Stream->Connection->Send.OrderedStreamBytesReceived += WriteLength;
This means the code fails to add the missing octets that become known when the FIN or RESET_STREAM reveals the stream’s final size, so the counter Connection->Send.OrderedStreamBytesReceived can undercount the stream’s final size for as long as there are gaps in the receive buffer, which violates the QUIC spec requirement to charge all bytes as soon as the final size is known.
In this case the receiver might issue a new MAX_DATA that gives the sender extra credit equal to the size of the holes, allowing the peer to exceed the advertised limit on other streams, i.e. over-granting connection credit.
Suggestion by o3 on what could make the code compliant with the RFC:
if (Frame->Fin && EndOffset > Stream->RecvTotalAccounted) {
uint64_t delta = EndOffset - Stream->RecvTotalAccounted;
Stream->Connection->Send.OrderedStreamBytesReceived += delta;
Stream->RecvTotalAccounted = EndOffset;
}