From: Hugo Landau Date: Mon, 30 Oct 2023 20:19:46 +0000 (+0000) Subject: QUIC APL: Optimise write buffer sizes automatically X-Git-Tag: openssl-3.3.0-alpha1~702 X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=commitdiff_plain;h=b119f8b892ea1dc5ee75f01a4632e7bc2b67323b QUIC APL: Optimise write buffer sizes automatically Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/22569) --- diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index d3310f8728..0c8e1b15a6 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -2177,6 +2177,58 @@ struct quic_write_again_args { int err; }; +/* + * Absolute maximum write buffer size, enforced to prevent a rogue peer from + * deliberately inducing DoS. This has been chosen based on the optimal buffer + * size for an RTT of 500ms and a bandwidth of 100 Mb/s. + */ +#define MAX_WRITE_BUF_SIZE (6 * 1024 * 1024) + +/* + * Ensure spare buffer space available (up until a limit, at least). + */ +QUIC_NEEDS_LOCK +static int sstream_ensure_spare(QUIC_SSTREAM *sstream, uint64_t spare) +{ + size_t cur_sz = ossl_quic_sstream_get_buffer_size(sstream); + size_t avail = ossl_quic_sstream_get_buffer_avail(sstream); + size_t spare_ = (spare > SIZE_MAX) ? SIZE_MAX : (size_t)spare; + size_t new_sz, growth; + + if (spare_ <= avail || cur_sz == MAX_WRITE_BUF_SIZE) + return 1; + + growth = spare_ - avail; + if (cur_sz + growth > MAX_WRITE_BUF_SIZE) + new_sz = MAX_WRITE_BUF_SIZE; + else + new_sz = cur_sz + growth; + + return ossl_quic_sstream_set_buffer_size(sstream, new_sz); +} + +/* + * Append to a QUIC_STREAM's QUIC_SSTREAM, ensuring buffer space is expanded + * as needed according to flow control. + */ +QUIC_NEEDS_LOCK +static int xso_sstream_append(QUIC_XSO *xso, const unsigned char *buf, + size_t len, size_t *actual_written) +{ + QUIC_SSTREAM *sstream = xso->stream->sstream; + uint64_t cur = ossl_quic_sstream_get_cur_size(sstream); + uint64_t cwm = ossl_quic_txfc_get_cwm(&xso->stream->txfc); + uint64_t permitted = (cwm >= cur ? cwm - cur : 0); + + if (len > permitted) + len = (size_t)permitted; + + if (!sstream_ensure_spare(sstream, len)) + return 0; + + return ossl_quic_sstream_append(sstream, buf, len, actual_written); +} + QUIC_NEEDS_LOCK static int quic_write_again(void *arg) { @@ -2195,8 +2247,7 @@ static int quic_write_again(void *arg) return -2; args->err = ERR_R_INTERNAL_ERROR; - if (!ossl_quic_sstream_append(args->xso->stream->sstream, - args->buf, args->len, &actual_written)) + if (!xso_sstream_append(args->xso, args->buf, args->len, &actual_written)) return -2; quic_post_write(args->xso, actual_written > 0, 0); @@ -2223,8 +2274,7 @@ static int quic_write_blocking(QCTX *ctx, const void *buf, size_t len, size_t actual_written = 0; /* First make a best effort to append as much of the data as possible. */ - if (!ossl_quic_sstream_append(xso->stream->sstream, buf, len, - &actual_written)) { + if (!xso_sstream_append(xso, buf, len, &actual_written)) { /* Stream already finished or allocation error. */ *written = 0; return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); @@ -2317,8 +2367,7 @@ static int quic_write_nonblocking_aon(QCTX *ctx, const void *buf, } /* First make a best effort to append as much of the data as possible. */ - if (!ossl_quic_sstream_append(xso->stream->sstream, actual_buf, actual_len, - &actual_written)) { + if (!xso_sstream_append(xso, actual_buf, actual_len, &actual_written)) { /* Stream already finished or allocation error. */ *written = 0; return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL); @@ -2379,7 +2428,7 @@ static int quic_write_nonblocking_epw(QCTX *ctx, const void *buf, size_t len, QUIC_XSO *xso = ctx->xso; /* Simple best effort operation. */ - if (!ossl_quic_sstream_append(xso->stream->sstream, buf, len, written)) { + if (!xso_sstream_append(xso, buf, len, written)) { /* Stream already finished or allocation error. */ *written = 0; return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);