From 2dbc39deacf9d5850eecef515cbc50331750dd22 Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Tue, 18 Apr 2023 19:30:54 +0100 Subject: QUIC APL: Add stream creation APIs Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/20765) --- include/internal/quic_channel.h | 7 +++++ include/internal/quic_stream_map.h | 3 ++ ssl/quic/quic_channel.c | 29 +++++++++++++++++++ ssl/quic/quic_channel_local.h | 8 ++++++ ssl/quic/quic_impl.c | 58 ++++++++++++++++++++++++++++++++++++-- ssl/quic/quic_local.h | 3 ++ 6 files changed, 105 insertions(+), 3 deletions(-) diff --git a/include/internal/quic_channel.h b/include/internal/quic_channel.h index d60e4010bd..7ccf6e0bb2 100644 --- a/include/internal/quic_channel.h +++ b/include/internal/quic_channel.h @@ -270,6 +270,13 @@ SSL *ossl_quic_channel_get0_ssl(QUIC_CHANNEL *ch); */ CRYPTO_MUTEX *ossl_quic_channel_get_mutex(QUIC_CHANNEL *ch); +/* + * Creates a new locally-initiated stream in the stream mapper, choosing an + * appropriate stream ID. If is_uni is 1, creates a unidirectional stream, else + * creates a bidirectional stream. + */ +QUIC_STREAM *ossl_quic_channel_new_stream(QUIC_CHANNEL *ch, int is_uni); + # endif #endif diff --git a/include/internal/quic_stream_map.h b/include/internal/quic_stream_map.h index f9a04723c1..0bdd7e88cf 100644 --- a/include/internal/quic_stream_map.h +++ b/include/internal/quic_stream_map.h @@ -101,6 +101,9 @@ struct quic_stream_st { /* A FIN has been retired from the rstream buffer. */ unsigned int recv_fin_retired : 1; + + /* The stream's XSO has been deleted. Pending GC. */ + unsigned int deleted : 1; }; /* diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c index 7ef66b234d..8e1d415935 100644 --- a/ssl/quic/quic_channel.c +++ b/ssl/quic/quic_channel.c @@ -2213,3 +2213,32 @@ SSL *ossl_quic_channel_get0_ssl(QUIC_CHANNEL *ch) { return ch->tls; } + +QUIC_STREAM *ossl_quic_channel_new_stream(QUIC_CHANNEL *ch, int is_uni) +{ + QUIC_STREAM *qs; + int type = 0; + uint64_t stream_id, *p_next_ordinal; + + type |= ch->is_server ? QUIC_STREAM_INITIATOR_SERVER + : QUIC_STREAM_INITIATOR_CLIENT; + + if (is_uni) { + p_next_ordinal = &ch->next_local_stream_ordinal_uni; + type |= QUIC_STREAM_DIR_UNI; + } else { + p_next_ordinal = &ch->next_local_stream_ordinal_bidi; + type |= QUIC_STREAM_DIR_BIDI; + } + + if (*p_next_ordinal >= ((uint64_t)1) << 62) + return NULL; + + stream_id = ((*p_next_ordinal) << 2) | type; + + if ((qs = ossl_quic_stream_map_alloc(&ch->qsm, stream_id, type)) == NULL) + return NULL; + + ++*p_next_ordinal; + return qs; +} diff --git a/ssl/quic/quic_channel_local.h b/ssl/quic/quic_channel_local.h index fbf16a93db..8534ae9835 100644 --- a/ssl/quic/quic_channel_local.h +++ b/ssl/quic/quic_channel_local.h @@ -159,6 +159,14 @@ struct quic_channel_st { /* Maximum active CID limit, as negotiated by transport parameters. */ uint64_t rx_active_conn_id_limit; + /* + * Used to allocate stream IDs. This is a stream ordinal, i.e., a stream ID + * without the low two bits designating type and initiator. Shift and or in + * the type bits to convert to a stream ID. + */ + uint64_t next_local_stream_ordinal_bidi; + uint64_t next_local_stream_ordinal_uni; + /* Valid if we are in the TERMINATING or TERMINATED states. */ QUIC_TERMINATE_CAUSE terminate_cause; diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index c02efed5f8..0dd6576633 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -257,10 +257,16 @@ SSL *ossl_quic_new(SSL_CTX *ctx) if (!create_channel(qc)) goto err; + if ((qc->default_xso = (QUIC_XSO *)ossl_quic_conn_stream_new(&qc->ssl, 0)) == NULL) + goto err; + return ssl_base; err: - SSL_free(qc->tls); + if (qc != NULL) { + ossl_quic_channel_free(qc->ch); + SSL_free(qc->tls); + } OPENSSL_free(qc); return NULL; } @@ -275,8 +281,39 @@ void ossl_quic_free(SSL *s) if (!expect_quic(s, &ctx)) return; + if (ctx.is_stream) { + /* + * When a QSSO is freed, the XSO is freed immediately, because the XSO + * itself only contains API personality layer data. However the + * underlying QUIC_STREAM is not freed immediately but is instead marked + * as deleted for later collection. + */ + + quic_lock(ctx.qc); + + assert(ctx.qc->num_xso > 0); + --ctx.qc->num_xso; + + ctx.xso->stream->deleted = 1; + + quic_unlock(ctx.qc); + + /* Note: SSL_free calls OPENSSL_free(xso) for us */ + return; + } + quic_lock(ctx.qc); + /* + * Free the default XSO, if any. The QUIC_STREAM is not deleted at this + * stage, but is freed during the channel free when the whole QSM is freed. + */ + if (ctx.qc->default_xso != NULL) + SSL_free(&ctx.qc->default_xso->ssl); + + /* Ensure we have no remaining XSOs. */ + assert(ctx.qc->num_xso == 0); + if (ctx.qc->is_thread_assisted && ctx.qc->started) { ossl_quic_thread_assist_wait_stopped(&ctx.qc->thread_assist); ossl_quic_thread_assist_cleanup(&ctx.qc->thread_assist); @@ -1047,12 +1084,20 @@ SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags) { QCTX ctx; QUIC_XSO *xso = NULL; + int is_uni = ((flags & SSL_STREAM_FLAG_UNI) != 0); if (!expect_quic_conn_only(s, &ctx)) return NULL; + quic_lock(ctx.qc); + + if (ossl_quic_channel_is_term_any(ctx.qc->ch)) { + QUIC_RAISE_NON_NORMAL_ERROR(ctx.qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); + goto err; + } + if ((xso = OPENSSL_zalloc(sizeof(*xso))) == NULL) - return NULL; + goto err; if (!ossl_ssl_init(&xso->ssl, s->ctx, s->method, SSL_TYPE_QUIC_XSO)) goto err; @@ -1061,11 +1106,18 @@ SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags) xso->blocking = ctx.qc->default_blocking; xso->ssl_mode = ctx.qc->default_ssl_mode; - xso->stream = NULL; /* TODO XXX ossl_quic_channel_new_stream */ + xso->stream = ossl_quic_channel_new_stream(ctx.qc->ch, is_uni); + if (xso->stream == NULL) + goto err; + + ++ctx.qc->num_xso; + + quic_unlock(ctx.qc); return &xso->ssl; err: OPENSSL_free(xso); + quic_unlock(ctx.qc); return NULL; } diff --git a/ssl/quic/quic_local.h b/ssl/quic/quic_local.h index 3906a20357..4b9d715c9b 100644 --- a/ssl/quic/quic_local.h +++ b/ssl/quic/quic_local.h @@ -138,6 +138,9 @@ struct quic_conn_st { OSSL_TIME (*override_now_cb)(void *arg); void *override_now_cb_arg; + /* Number of XSOs allocated. Includes the default XSO, if any. */ + size_t num_xso; + /* Have we started? */ unsigned int started : 1; -- cgit v1.2.1