QUIC APL: Optimise write buffer sizes automatically
authorHugo Landau <hlandau@openssl.org>
Mon, 30 Oct 2023 20:19:46 +0000 (20:19 +0000)
committerHugo Landau <hlandau@openssl.org>
Wed, 1 Nov 2023 15:35:19 +0000 (15:35 +0000)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22569)

ssl/quic/quic_impl.c

index d3310f87288e6e5cf8e9417f1dfdd8732b1c12dd..0c8e1b15a664d60228e8d01ccb5efbf67331a818 100644 (file)
@@ -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);