]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: h1: keep accepting unsafe violations after a parser reset
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 7 Oct 2026 08:34:08 +0000 (10:34 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 16 Oct 2026 17:07:18 +0000 (19:07 +0200)
The H1 parser relies on h1m->err_pos being preset to -1 instead of -2 to
accept some violations ("option accept-unsafe-violations-in-http-request"
and "option accept-unsafe-violations-in-http-response"). The H1 mux sets it
when the H1 stream is created. However the H1 message is reset in a few
places using h1m_init_req() or h1m_init_res(), which set err_pos back to -2:

  - in h1_process_demux(), when a 1xx interim response was received, to
    parse the final response;

  - in h1_postparse_req_hdrs() and h1_postparse_res_hdrs(), when the headers
    do not fit in the output buffer and the parsing must be restarted later.

As a result, the strict mode is silently enforced on the final response
following a 1xx interim response (or after an output full condition), and a
response accepted otherwise is rejected with a 502.

To fix the issue, the lenient mode is restored after these resets: from the
proxy's options in the mux, and from the previous err_pos value in the
h1_postparse_* functions.

This should be backported to all stable versions.

src/h1_htx.c
src/mux_h1.c

index 60cee11ce395217e32175f1d4a5ffa7e8328acb8..aaaca5ad4008ac1b9d609636da6f48d4226f052a 100644 (file)
@@ -178,6 +178,7 @@ static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx
        struct htx_sl *sl;
        struct ist meth, uri, vsn;
        unsigned int flags = 0;
+       int lenient;
 
        /* <h1sl> is always defined for a request */
        meth = h1sl->rq.m;
@@ -268,8 +269,11 @@ static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx
         * corresponding flags must be preserved for the next parsing.
         */
        flags = h1m->flags & (H1_MF_CONN_CLO|H1_MF_CONN_KAL);
+       lenient = (h1m->err_pos >= -1);
        h1m_init_req(h1m);
        h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR|flags);
+       if (lenient)
+               h1m->err_pos = -1; /* keep accepting unsafe violations */
        return -2;
   error:
        h1m->err_pos = h1m->next;
@@ -290,6 +294,7 @@ static int h1_postparse_res_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx
        struct ist vsn, status, reason;
        unsigned int flags = 0;
        uint16_t code = 0;
+       int lenient;
 
        if (h1sl) {
                /* For HTTP responses, the start-line was parsed */
@@ -393,8 +398,11 @@ static int h1_postparse_res_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx
         * corresponding flags must be preserved for the next parsing.
         */
        flags = h1m->flags & (H1_MF_CONN_CLO|H1_MF_CONN_KAL);
+       lenient = (h1m->err_pos >= -1);
        h1m_init_res(h1m);
        h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR|flags);
+       if (lenient)
+               h1m->err_pos = -1; /* keep accepting unsafe violations */
        return -2;
   error:
        h1m->err_pos = h1m->next;
index 7e6cf2bf265d2c41f2c3caf065c0765f10708d82..82b29cb480d8644ad42c612507433b35aa8a53cb 100644 (file)
@@ -2230,6 +2230,8 @@ static size_t h1_process_demux(struct h1c *h1c, struct buffer *buf, size_t count
                            h1s->status < 200 && (h1s->status == 100 || h1s->status >= 102)) {
                                h1m_init_res(&h1s->res);
                                h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
+                               if (h1c->px->options2 & PR_O2_RSPBUG_OK)
+                                       h1m->err_pos = -1;
                                TRACE_STATE("1xx response rcvd", H1_EV_RX_DATA|H1_EV_RX_HDRS, h1c->conn, h1s);
                        }
                }