]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: mux-h2: report glitches on early RST_STREAM
authorWilly Tarreau <w@1wt.eu>
Thu, 19 Mar 2026 17:00:20 +0000 (18:00 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 30 Mar 2026 14:32:21 +0000 (16:32 +0200)
We leverage the SE_FL_APP_STARTED flag to detect whether the application
layer had a chance to run or not when an RST_STREAM is received. This
allows us to triage RST_STREAM between regular ones and harmful ones,
and to count glitches for them. It reveals extremely effective at
detecting fast HEADERS+RST pairs.

It could be useful to backport it to 3.2, though it depends on these
two previous patches to be backported first (the first one was already
planned and the second one is harmless, though will require to drop
the haterm changes):

  BUG/MINOR: stconn: Always declare the SC created from healthchecks as a back SC
  MINOR: stconn: flag the stream endpoint descriptor when the app has started

src/mux_h2.c

index d4d8f32758290f7e30648c185fec85cdf370d8d5..05ecf250dc8986d371d38637c11420a2838a8865 100644 (file)
@@ -3453,6 +3453,7 @@ static int h2c_handle_priority(struct h2c *h2c)
 static int h2c_handle_rst_stream(struct h2c *h2c, struct h2s *h2s)
 {
        int rst_code;
+       int ret = 1;
 
        TRACE_ENTER(H2_EV_RX_FRAME|H2_EV_RX_RST|H2_EV_RX_EOI, h2c->conn, h2s);
 
@@ -3481,6 +3482,16 @@ static int h2c_handle_rst_stream(struct h2c *h2c, struct h2s *h2s)
 
        if (h2s_sc(h2s)) {
                se_fl_set_error(h2s->sd);
+
+               if (unlikely(!se_fl_test(h2s->sd, SE_FL_APP_STARTED))) {
+                       /* the application layer has not yet started to read! */
+                       TRACE_STATE("received early RST_STREAM", H2_EV_RX_FRAME|H2_EV_RX_RST, h2c->conn);
+                       if (h2c_report_glitch(h2c, 1, "received early RST_STREAM, attack suspected")) {
+                               TRACE_DEVEL("too many glitches, leaving on error", H2_EV_RX_FRAME|H2_EV_RX_RST, h2c->conn, h2s);
+                               ret = 0; // report the error
+                       }
+               }
+
                se_report_term_evt(h2s->sd, se_tevt_type_rst_rcvd);
                if (!h2s->sd->abort_info.info) {
                        h2s->sd->abort_info.info = (SE_ABRT_SRC_MUX_H2 << SE_ABRT_SRC_SHIFT);
@@ -3491,7 +3502,7 @@ static int h2c_handle_rst_stream(struct h2c *h2c, struct h2s *h2s)
 
        h2s->flags |= H2_SF_RST_RCVD;
        TRACE_LEAVE(H2_EV_RX_FRAME|H2_EV_RX_RST|H2_EV_RX_EOI, h2c->conn, h2s);
-       return 1;
+       return ret;
 }
 
 /* processes a HEADERS frame. Returns h2s on success or NULL on missing data.