]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
http2: add on_invalid_frame callback for error detection
authorStefan Eissing <stefan@eissing.org>
Tue, 4 Mar 2025 10:48:04 +0000 (11:48 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 5 Mar 2025 09:59:19 +0000 (10:59 +0100)
When the server sends HEADER/CONTINUATION frames that exceed nghttp2's
size, this error is being reported via the on_invalid_frame_recv
callback. Without registering there, it will go unnoticed.

RST the stream when such a frame is encountered.

Closes #16544

lib/http2.c
tests/http/test_07_upload.py

index ef95d03e00dfcf03236e11c195cfd14e8cd1388c..c1b7876227717f91f1a709e0dac580ed796e9506 100644 (file)
@@ -480,6 +480,10 @@ static ssize_t send_callback(nghttp2_session *h2,
                              void *userp);
 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
                          void *userp);
+static int cf_h2_on_invalid_frame_recv(nghttp2_session *session,
+                                       const nghttp2_frame *frame,
+                                       int lib_error_code,
+                                       void *user_data);
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
                          void *userp);
@@ -520,6 +524,8 @@ static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
 
   nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
   nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
+  nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(cbs,
+    cf_h2_on_invalid_frame_recv);
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send);
 #endif
@@ -1379,6 +1385,39 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
   return on_stream_frame(cf, data_s, frame) ? NGHTTP2_ERR_CALLBACK_FAILURE : 0;
 }
 
+static int cf_h2_on_invalid_frame_recv(nghttp2_session *session,
+                                       const nghttp2_frame *frame,
+                                       int ngerr, void *userp)
+{
+  struct Curl_cfilter *cf = userp;
+  struct cf_h2_ctx *ctx = cf->ctx;
+  struct Curl_easy *data;
+  int32_t stream_id = frame->hd.stream_id;
+
+  data = nghttp2_session_get_stream_user_data(session, stream_id);
+  if(data) {
+    struct h2_stream_ctx *stream;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+    char buffer[256];
+    int len;
+    len = fr_print(frame, buffer, sizeof(buffer)-1);
+    buffer[len] = 0;
+    failf(data, "[HTTP2] [%d] received invalid frame: %s, error %d: %s",
+          stream_id, buffer, ngerr, nghttp2_strerror(ngerr));
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+    stream = H2_STREAM_CTX(ctx, data);
+    if(stream) {
+      nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
+                                stream->id, NGHTTP2_STREAM_CLOSED);
+      stream->error = ngerr;
+      stream->closed = TRUE;
+      stream->reset = TRUE;
+      return 0;  /* keep the connection alive */
+    }
+  }
+  return NGHTTP2_ERR_CALLBACK_FAILURE;
+}
+
 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
                               int32_t stream_id,
                               const uint8_t *mem, size_t len, void *userp)
index 53e5a00a35d2b22dc56348ca2b566d9e24c39aa2..438873e0ea1d0fd0c767db06ee1612623b2c5c2c 100644 (file)
@@ -549,7 +549,7 @@ class TestUpload:
         if r.exit_code == 18: # PARTIAL_FILE is always ok
             pass
         elif proto == 'h2':
-            r.check_exit_code(92)  # CURLE_HTTP2_STREAM also ok
+            r.check_exit_code(16)  # CURLE_HTTP2 also ok
         elif proto == 'h3':
             r.check_exit_code(95)  # CURLE_HTTP3 also ok
         else: