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.
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;
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;
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) {