]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: mux-h1: Fix loop if server closes its connection with unparsed data
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 11 Dec 2018 15:12:31 +0000 (16:12 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 13 Dec 2018 16:32:15 +0000 (17:32 +0100)
A first patch was pushed to fix this bug if it happens during the headers
parsing. But it is also possible to hit the bug during the parsing of
chunks. For instance, if the server sends only part of the trailers, some data
remains unparsed. So it the server closes its connection without sending the end
of the response, we fall back again into an infinite loop.

The fix contains in 2 parts. First, we block the receive if a read0 or an error
is detected on the connection, independently if the input buffer is empty or
not. Then, the flags CS_FL_RCV_MORE and CL_FL_WANT_ROOM are always reset when
input data are processed. We set them again only when necessary.

src/mux_h1.c

index 5ca3c28a0fc8dbb547d34b9a743352f552a38f89..dfe9fd4b3830331b5f810969aeb96a14505cb433 100644 (file)
@@ -131,10 +131,10 @@ static void h1_shutw_conn(struct connection *conn);
  */
 static inline int h1_recv_allowed(const struct h1c *h1c)
 {
-       if (b_data(&h1c->ibuf) == 0 &&
-           (h1c->flags & (H1C_F_CS_ERROR|H1C_F_CS_SHUTW) ||
-            h1c->conn->flags & CO_FL_ERROR ||
-            conn_xprt_read0_pending(h1c->conn)))
+       if (b_data(&h1c->ibuf) == 0 && (h1c->flags & (H1C_F_CS_ERROR|H1C_F_CS_SHUTW)))
+               return 0;
+
+       if (h1c->conn->flags & CO_FL_ERROR || conn_xprt_read0_pending(h1c->conn))
                return 0;
 
        if (!(h1c->flags & (H1C_F_IN_ALLOC|H1C_F_IN_FULL|H1C_F_IN_BUSY)))
@@ -1325,19 +1325,17 @@ static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, int flags)
                tasklet_wakeup(h1c->wait_event.task);
        }
 
-       if (b_data(&h1c->ibuf)) {
-               if (!htx_is_empty(htx))
-                       h1s->cs->flags |= CS_FL_RCV_MORE | CS_FL_WANT_ROOM;
-       }
-       else {
+       h1s->cs->flags &= ~(CS_FL_RCV_MORE | CS_FL_WANT_ROOM);
+
+       if (!b_data(&h1c->ibuf)) {
                h1_release_buf(h1c, &h1c->ibuf);
                h1_sync_messages(h1c);
-               h1s->cs->flags &= ~(CS_FL_RCV_MORE | CS_FL_WANT_ROOM);
        }
+       else if (!htx_is_empty(htx))
+               h1s->cs->flags |= CS_FL_RCV_MORE | CS_FL_WANT_ROOM;
 
        if ((h1s->cs->flags & CS_FL_REOS) && (!b_data(&h1c->ibuf) || htx_is_empty(htx))) {
                h1s->cs->flags |= CS_FL_EOS;
-               h1s->cs->flags &= ~(CS_FL_RCV_MORE | CS_FL_WANT_ROOM);
        }
 
        return total;
@@ -1673,10 +1671,12 @@ static int h1_recv(struct h1c *h1c)
                }
        }
 
-       if (h1_recv_allowed(h1c) && buf_room_for_htx_data(&h1c->ibuf))
-               conn->xprt->subscribe(conn, SUB_CAN_RECV, &h1c->wait_event);
-       else
+       if (!h1_recv_allowed(h1c) || !buf_room_for_htx_data(&h1c->ibuf)) {
                rcvd = 1;
+               goto end;
+       }
+
+       conn->xprt->subscribe(conn, SUB_CAN_RECV, &h1c->wait_event);
 
   end:
        if ((ret > 0 || (conn->flags & CO_FL_ERROR) ||