QUIC RXDP: Ensure all stream-related frames autocreate a stream

RFC requirement.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20856)
This commit is contained in:
Hugo Landau 2023-04-28 16:56:34 +01:00
parent 3dde3435ab
commit dea57ecf3d

View File

@ -29,6 +29,10 @@
* pointer argument, the few that aren't ACK eliciting will not. This makes
* them a verifiable pattern against tables where this is specified.
*/
static int depack_do_implicit_stream_create(QUIC_CHANNEL *ch,
uint64_t stream_id,
uint64_t frame_type,
QUIC_STREAM **result);
static int depack_do_frame_padding(PACKET *pkt)
{
@ -110,15 +114,13 @@ static int depack_do_frame_reset_stream(PACKET *pkt,
/* This frame makes the packet ACK eliciting */
ackm_data->is_ack_eliciting = 1;
stream = ossl_quic_stream_map_get_by_id(&ch->qsm, frame_data.stream_id);
if (stream == NULL) {
ossl_quic_channel_raise_protocol_error(ch,
QUIC_ERR_STREAM_STATE_ERROR,
OSSL_QUIC_FRAME_TYPE_RESET_STREAM,
"RESET_STREAM frame for "
"nonexistent stream");
return 0;
}
if (!depack_do_implicit_stream_create(ch, frame_data.stream_id,
OSSL_QUIC_FRAME_TYPE_RESET_STREAM,
&stream))
return 0; /* error already raised for us */
if (stream == NULL)
return 1; /* old deleted stream, not a protocol violation, ignore */
if (stream->rstream == NULL) {
ossl_quic_channel_raise_protocol_error(ch,
@ -154,15 +156,13 @@ static int depack_do_frame_stop_sending(PACKET *pkt,
/* This frame makes the packet ACK eliciting */
ackm_data->is_ack_eliciting = 1;
stream = ossl_quic_stream_map_get_by_id(&ch->qsm, frame_data.stream_id);
if (stream == NULL) {
ossl_quic_channel_raise_protocol_error(ch,
QUIC_ERR_STREAM_STATE_ERROR,
OSSL_QUIC_FRAME_TYPE_STOP_SENDING,
"STOP_SENDING frame for "
"nonexistent stream");
return 0;
}
if (!depack_do_implicit_stream_create(ch, frame_data.stream_id,
OSSL_QUIC_FRAME_TYPE_STOP_SENDING,
&stream))
return 0; /* error already raised for us */
if (stream == NULL)
return 1; /* old deleted stream, not a protocol violation, ignore */
if (stream->sstream == NULL) {
ossl_quic_channel_raise_protocol_error(ch,
@ -242,6 +242,142 @@ static int depack_do_frame_new_token(PACKET *pkt, QUIC_CHANNEL *ch,
return 1;
}
/*
* Returns 1 if no protocol violation has occurred. In this case *result will be
* non-NULL unless this is an old deleted stream and we should ignore the frame
* causing this function to be called. Returns 0 on protocol violation.
*/
static int depack_do_implicit_stream_create(QUIC_CHANNEL *ch,
uint64_t stream_id,
uint64_t frame_type,
QUIC_STREAM **result)
{
QUIC_STREAM *stream;
uint64_t peer_role, stream_ordinal;
uint64_t *p_next_ordinal_local, *p_next_ordinal_remote;
QUIC_RXFC *max_streams_fc;
int is_uni, is_remote_init;
stream = ossl_quic_stream_map_get_by_id(&ch->qsm, stream_id);
if (stream != NULL) {
*result = stream;
return 1;
}
/*
* If we do not yet have a stream with the given ID, there are three
* possibilities:
*
* (a) The stream ID is for a remotely-created stream and the peer
* is creating a stream.
*
* (b) The stream ID is for a locally-created stream which has
* previously been deleted.
*
* (c) The stream ID is for a locally-created stream which does
* not exist yet. This is a protocol violation and we must
* terminate the connection in this case.
*
* We distinguish between (b) and (c) using the stream ID allocator
* variable. Since stream ordinals are allocated monotonically, we
* simply determine if the stream ordinal is in the future.
*/
peer_role = ch->is_server
? QUIC_STREAM_INITIATOR_CLIENT
: QUIC_STREAM_INITIATOR_SERVER;
is_remote_init = ((stream_id & QUIC_STREAM_INITIATOR_MASK) == peer_role);
is_uni = ((stream_id & QUIC_STREAM_DIR_MASK) == QUIC_STREAM_DIR_UNI);
stream_ordinal = stream_id >> 2;
if (is_remote_init) {
/*
* Peer-created stream which does not yet exist. Create it. QUIC stream
* ordinals within a given stream type MUST be used in sequence and
* receiving a STREAM frame for ordinal n must implicitly create streams
* with ordinals [0, n) within that stream type even if no explicit
* STREAM frames are received for those ordinals.
*/
p_next_ordinal_remote = is_uni
? &ch->next_remote_stream_ordinal_uni
: &ch->next_remote_stream_ordinal_bidi;
/* Check this isn't violating stream count flow control. */
max_streams_fc = is_uni
? &ch->max_streams_uni_rxfc
: &ch->max_streams_bidi_rxfc;
if (!ossl_quic_rxfc_on_rx_stream_frame(max_streams_fc,
stream_ordinal + 1,
/*is_fin=*/0)) {
ossl_quic_channel_raise_protocol_error(ch,
QUIC_ERR_INTERNAL_ERROR,
frame_type,
"internal error (stream count RXFC)");
return 0;
}
if (ossl_quic_rxfc_get_error(max_streams_fc, 0) != QUIC_ERR_NO_ERROR) {
ossl_quic_channel_raise_protocol_error(ch, QUIC_ERR_STREAM_LIMIT_ERROR,
frame_type,
"exceeded maximum allowed streams");
return 0;
}
/*
* Create the named stream and any streams coming before it yet to be
* created.
*/
while (*p_next_ordinal_remote <= stream_ordinal) {
uint64_t cur_stream_id = (*p_next_ordinal_remote << 2) |
(stream_id
& (QUIC_STREAM_DIR_MASK | QUIC_STREAM_INITIATOR_MASK));
stream = ossl_quic_channel_new_stream_remote(ch, cur_stream_id);
if (stream == NULL) {
ossl_quic_channel_raise_protocol_error(ch,
QUIC_ERR_INTERNAL_ERROR,
frame_type,
"internal error (stream allocation)");
return 0;
}
++*p_next_ordinal_remote;
}
*result = stream;
} else {
/* Locally-created stream which does not yet exist. */
p_next_ordinal_local = is_uni
? &ch->next_local_stream_ordinal_uni
: &ch->next_local_stream_ordinal_bidi;
if (stream_ordinal >= *p_next_ordinal_local) {
/*
* We never created this stream yet, this is a protocol
* violation.
*/
ossl_quic_channel_raise_protocol_error(ch,
QUIC_ERR_STREAM_STATE_ERROR,
frame_type,
"STREAM frame for nonexistent "
"stream");
return 0;
}
/*
* Otherwise this is for an old locally-initiated stream which we
* have subsequently deleted. Ignore the data; it may simply be a
* retransmission. We already take care of notifying the peer of the
* termination of the stream during the stream deletion lifecycle.
*/
*result = NULL;
}
return 1;
}
static int depack_do_frame_stream(PACKET *pkt, QUIC_CHANNEL *ch,
OSSL_QRX_PKT *parent_pkt,
OSSL_ACKM_RX_PKT *ackm_data,
@ -262,130 +398,16 @@ static int depack_do_frame_stream(PACKET *pkt, QUIC_CHANNEL *ch,
/* This frame makes the packet ACK eliciting */
ackm_data->is_ack_eliciting = 1;
stream = ossl_quic_stream_map_get_by_id(&ch->qsm, frame_data.stream_id);
if (stream == NULL) {
uint64_t peer_role, stream_ordinal;
uint64_t *p_next_ordinal_local, *p_next_ordinal_remote;
QUIC_RXFC *max_streams_fc;
int is_uni;
if (!depack_do_implicit_stream_create(ch, frame_data.stream_id,
frame_type, &stream))
return 0; /* protocol error raised by above call */
if (stream == NULL)
/*
* If we do not yet have a stream with the given ID, there are three
* possibilities:
*
* (a) The stream ID is for a remotely-created stream and the peer
* is creating a stream.
*
* (b) The stream ID is for a locally-created stream which has
* previously been deleted.
*
* (c) The stream ID is for a locally-created stream which does
* not exist yet. This is a protocol violation and we must
* terminate the connection in this case.
*
* We distinguish between (b) and (c) using the stream ID allocator
* variable. Since stream ordinals are allocated monotonically, we
* simply determine if the stream ordinal is in the future.
* Data for old stream which is not a protocol violation but should be
* ignored, so stop here.
*/
peer_role = ch->is_server
? QUIC_STREAM_INITIATOR_CLIENT
: QUIC_STREAM_INITIATOR_SERVER;
is_uni = ((frame_data.stream_id & QUIC_STREAM_DIR_MASK)
== QUIC_STREAM_DIR_UNI);
stream_ordinal = frame_data.stream_id >> 2;
if ((frame_data.stream_id & QUIC_STREAM_INITIATOR_MASK) == peer_role) {
/*
* Peer-created stream which does not yet exist. Create it. QUIC
* stream ordinals within a given stream type MUST be used in
* sequence and receiving a STREAM frame for ordinal n must
* implicitly create streams with ordinals [0, n) within that stream
* type even if no explicit STREAM frames are received for those
* ordinals.
*/
p_next_ordinal_remote = is_uni
? &ch->next_remote_stream_ordinal_uni
: &ch->next_remote_stream_ordinal_bidi;
/* Check this isn't violating stream count flow control. */
max_streams_fc = is_uni
? &ch->max_streams_uni_rxfc
: &ch->max_streams_bidi_rxfc;
if (!ossl_quic_rxfc_on_rx_stream_frame(max_streams_fc,
stream_ordinal + 1,
/*is_fin=*/0)) {
ossl_quic_channel_raise_protocol_error(ch,
QUIC_ERR_INTERNAL_ERROR,
frame_type,
"internal error (stream count RXFC)");
return 0;
}
if (ossl_quic_rxfc_get_error(max_streams_fc, 0) != QUIC_ERR_NO_ERROR) {
ossl_quic_channel_raise_protocol_error(ch, QUIC_ERR_STREAM_LIMIT_ERROR,
frame_type,
"exceeded maximum allowed streams");
return 0;
}
/*
* Create the named stream and any streams coming before it yet to
* be created.
*/
while (*p_next_ordinal_remote <= stream_ordinal) {
uint64_t stream_id = (*p_next_ordinal_remote << 2) |
(frame_data.stream_id
& (QUIC_STREAM_DIR_MASK | QUIC_STREAM_INITIATOR_MASK));
stream = ossl_quic_channel_new_stream_remote(ch, stream_id);
if (stream == NULL) {
ossl_quic_channel_raise_protocol_error(ch,
QUIC_ERR_INTERNAL_ERROR,
frame_type,
"internal error (stream allocation)");
return 0;
}
++*p_next_ordinal_remote;
}
/*
* Fallthrough to processing of stream data for newly created
* stream.
*/
} else {
/* Locally-created stream which does not yet exist. */
p_next_ordinal_local = is_uni
? &ch->next_local_stream_ordinal_uni
: &ch->next_local_stream_ordinal_bidi;
if (stream_ordinal >= *p_next_ordinal_local) {
/*
* We never created this stream yet, this is a protocol
* violation.
*/
ossl_quic_channel_raise_protocol_error(ch,
QUIC_ERR_STREAM_STATE_ERROR,
frame_type,
"STREAM frame for nonexistent "
"stream");
return 0;
}
/*
* Otherwise this is for an old locally-initiated stream which we
* have subsequently deleted. Ignore the data; it may simply be a
* retransmission. We already take care of notifying the peer of the
* termination of the stream during the stream deletion lifecycle.
*/
return 1;
}
}
return 1;
if (stream->rstream == NULL) {
ossl_quic_channel_raise_protocol_error(ch,
@ -501,15 +523,13 @@ static int depack_do_frame_max_stream_data(PACKET *pkt,
/* This frame makes the packet ACK eliciting */
ackm_data->is_ack_eliciting = 1;
stream = ossl_quic_stream_map_get_by_id(&ch->qsm, stream_id);
if (stream == NULL) {
ossl_quic_channel_raise_protocol_error(ch,
QUIC_ERR_STREAM_STATE_ERROR,
OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA,
"MAX_STREAM_DATA for nonexistent "
"stream");
return 0;
}
if (!depack_do_implicit_stream_create(ch, stream_id,
OSSL_QUIC_FRAME_TYPE_MAX_STREAM_DATA,
&stream))
return 0; /* error already raised for us */
if (stream == NULL)
return 1; /* old deleted stream, not a protocol violation, ignore */
if (stream->sstream == NULL) {
ossl_quic_channel_raise_protocol_error(ch,
@ -604,6 +624,7 @@ static int depack_do_frame_stream_data_blocked(PACKET *pkt,
{
uint64_t stream_id = 0;
uint64_t max_data = 0;
QUIC_STREAM *stream;
if (!ossl_quic_wire_decode_frame_stream_data_blocked(pkt, &stream_id,
&max_data)) {
@ -617,6 +638,15 @@ static int depack_do_frame_stream_data_blocked(PACKET *pkt,
/* This frame makes the packet ACK eliciting */
ackm_data->is_ack_eliciting = 1;
/*
* This is an informative/debugging frame, so we don't have to do anything,
* but it does trigger stream creation.
*/
if (!depack_do_implicit_stream_create(ch, stream_id,
OSSL_QUIC_FRAME_TYPE_STREAM_DATA_BLOCKED,
&stream))
return 0; /* error already raised for us */
/* No-op - informative/debugging frame. */
return 1;
}