]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: mux-h1: close the connection on a short content-length
authorOlivier Houchard <ohouchard@haproxy.com>
Thu, 13 Aug 2026 18:58:18 +0000 (20:58 +0200)
committerOlivier Houchard <cognet@ci0.org>
Tue, 25 Aug 2026 13:52:24 +0000 (15:52 +0200)
When a fixed-length message completes while curr_len is still non-zero,
fewer bytes than announced were sent and the peer keeps waiting, so
reusing the connection desynchronizes the next request (request
smuggling). In h1s_destroy(), when either the request or the response
still expects data, flag the connection on error, unless the message is
legitimately bodyless. It is then closed instead of being returned to
the idle pool.

This can be reproduced with an H2 frontend, that will send less data
than announced before sending an end of stream, to an H1 backend that
replies before it received the whole request.

This should be backported up to 2.8.

Many thanks to truff@projet7.org for reporting the issue and providing
an initial fix.

src/mux_h1.c

index 3d95f042f367ac45f147cd10dab10846fbdcf5a3..eda34f252df6b38257993f423adfce4e2486f2d8 100644 (file)
@@ -1084,6 +1084,15 @@ static void h1s_destroy(struct h1s *h1s)
                                H1C_F_IN_SALLOC|H1C_F_IN_SMAYALLOC|
                                H1C_F_CO_MSG_MORE|H1C_F_CO_STREAMER);
 
+               /*
+                * If we reached the end of the stream and sent less bits than
+                * announced in Content-Length, we have little choice but to
+                * close the connection, as the peer will expect more.
+                */
+               if (((h1s->req.flags & H1_MF_CLEN) && h1s->req.curr_len && !(h1s->flags & H1S_F_BODYLESS_REQ)) ||
+                   ((h1s->res.flags & H1_MF_CLEN) && h1s->res.curr_len && !(h1s->flags & H1S_F_BODYLESS_RESP)))
+                       h1c->flags |= H1C_F_ERROR;
+
                if (!(h1c->flags & (H1C_F_EOS|H1C_F_ERR_PENDING|H1C_F_ERROR|H1C_F_ABRT_PENDING|H1C_F_ABRTED)) &&  /* No error/read0/abort */
                    h1_is_alive(h1c) &&                                                      /* still alive */
                    (h1s->flags & H1S_F_WANT_KAL) &&                                         /* K/A possible */