]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: h1: don't use uninitialized start line pointers when restarting
authorChristopher Faulet <cfaulet@haproxy.com>
Wed, 7 Oct 2026 08:57:06 +0000 (10:57 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 16 Oct 2026 17:10:26 +0000 (19:10 +0200)
When h1_headers_to_hdr_list() is called again on an incomplete message,
it resumes from the state where it stopped, scans until the end of the
headers, then restarts the parsing from the beginning. During this first
scan, the start line elements (union h1_sl) were not set by the current
call and are uninitialized. However two places in the response start
line parsing still use them:

  - in H1_MSG_RPVER, sl.st.v.ptr is used to compute the version length
    and to read the version's digits to set H1_MF_VER_11. The result is
    discarded on restart, but this reads from a random pointer if the
    computed length is 8;

  - in H1_MSG_RPCODE, the number of digits is computed from sl.st.c.ptr
    since commit 8bdcc5516 ("BUG/MEDIUM: h1: limit status codes to 3
    digits by default"). With a random pointer, a valid response whose
    status code is split across two reads (e.g. "HTTP/1.1 2" then
    "00 OK...") may be rejected as invalid in strict mode, or its error
    position captured in lenient mode, depending on the stack contents.

Now the start line pointers are initialized to the beginning of the
message, and these checks are skipped during the first scan of a
restart, since they are performed again during the full parsing.

This only concerns responses, so it can only be triggered by a server.
It must be backported wherever commit 8bdcc5516 was backported. The RPVER
part may be backported further but it is harmless.

src/h1.c

index 2d33bd31682d8107ff94a2e9965b1a6de3e0484c..814a5ea4d0044055c77a36f7239add31280b6d3c 100644 (file)
--- a/src/h1.c
+++ b/src/h1.c
@@ -454,6 +454,13 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
                        restarting = 1;
        }
 
+       /* When restarting from an interrupted state, the start line elements
+        * were not set by this call. They are not used in this case but let's
+        * make sure they never point to random locations (rq and st members
+        * are at the same places).
+        */
+       sl.rq.m = sl.rq.u = sl.rq.v = ist2(start, 0);
+
        ptr   = start + h1m->next;
        end   = stop;
 
@@ -705,7 +712,7 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
                if (likely(HTTP_IS_SPHT(*ptr))) {
                        sl.st.v.len = ptr - sl.st.v.ptr;
 
-                       if ((sl.st.v.len == 8) &&
+                       if (!restarting && (sl.st.v.len == 8) &&
                            (*(sl.st.v.ptr + 5) > '1' ||
                             (*(sl.st.v.ptr + 5) == '1' && *(sl.st.v.ptr + 7) >= '1')))
                                h1m->flags |= H1_MF_VER_11;
@@ -731,8 +738,8 @@ int h1_headers_to_hdr_list(char *start, const char *stop,
        case H1_MSG_RPCODE:
        http_msg_rpcode:
                if (likely(HTTP_IS_DIGIT(*ptr))) {
-                       if (ptr - sl.st.c.ptr >= 3) {
-                               /* more than 3 digits */
+                       if (!restarting && ptr - sl.st.c.ptr >= 3) {
+                               /* more than 3 digits (checked again after a restart) */
                                if (h1m->err_pos == -1) /* only capture the error pointer */
                                        h1m->err_pos = ptr - start + skip;
                                else if (h1m->err_pos < -1 || sl.st.status >= ((uint16_t)~0 - 9) / 10) {