From c866db09111b700e8fc93d86347b19a27e138010 Mon Sep 17 00:00:00 2001 From: Christian Lachner <gladiac@gmail.com> Date: Sun, 29 Apr 2018 10:04:49 +0200 Subject: [PATCH] haproxy: Update all patches for HAProxy v1.8.8 - Add new patches (see https://www.haproxy.org/bugs/bugs-1.8.8.html) - Raise patch-level to 02 Signed-off-by: Christian Lachner <gladiac@gmail.com> --- net/haproxy/Makefile | 2 +- ...issing-HA_SPIN_INIT-in-pat_ref_newid.patch | 26 +++ ...-trying-to-read-from-a-closed-socket.patch | 87 ++++++++++ ...idle-Ti-is-not-set-for-some-requests.patch | 50 ++++++ ...gmentation-fault-if-a-Lua-task-exits.patch | 48 +++++ ...nce-of-CONNECT-and-or-content-length.patch | 147 ++++++++++++++++ ...-support-for-chunked-encoded-uploads.patch | 164 ++++++++++++++++++ ...s-tasks-sticky-to-the-current-thread.patch | 36 ++++ 8 files changed, 559 insertions(+), 1 deletion(-) create mode 100644 net/haproxy/patches/0000-BUG-MINOR-pattern-Add-a-missing-HA_SPIN_INIT-in-pat_ref_newid.patch create mode 100644 net/haproxy/patches/0001-BUG-MAJOR-channel-Fix-crash-when-trying-to-read-from-a-closed-socket.patch create mode 100644 net/haproxy/patches/0002-BUG-MINOR-log-t_idle-Ti-is-not-set-for-some-requests.patch create mode 100644 net/haproxy/patches/0003-BUG-MEDIUM-lua-Fix-segmentation-fault-if-a-Lua-task-exits.patch create mode 100644 net/haproxy/patches/0004-MINOR-h2-detect-presence-of-CONNECT-and-or-content-length.patch create mode 100644 net/haproxy/patches/0005-BUG-MEDIUM-h2-implement-missing-support-for-chunked-encoded-uploads.patch create mode 100644 net/haproxy/patches/0006-BUG-MINOR-lua-threads-Make-luas-tasks-sticky-to-the-current-thread.patch diff --git a/net/haproxy/Makefile b/net/haproxy/Makefile index 6f07eaf20..44544c97f 100644 --- a/net/haproxy/Makefile +++ b/net/haproxy/Makefile @@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=haproxy PKG_VERSION:=1.8.8 -PKG_RELEASE:=01 +PKG_RELEASE:=02 PKG_SOURCE:=haproxy-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://www.haproxy.org/download/1.8/src/ diff --git a/net/haproxy/patches/0000-BUG-MINOR-pattern-Add-a-missing-HA_SPIN_INIT-in-pat_ref_newid.patch b/net/haproxy/patches/0000-BUG-MINOR-pattern-Add-a-missing-HA_SPIN_INIT-in-pat_ref_newid.patch new file mode 100644 index 000000000..eb3a18a10 --- /dev/null +++ b/net/haproxy/patches/0000-BUG-MINOR-pattern-Add-a-missing-HA_SPIN_INIT-in-pat_ref_newid.patch @@ -0,0 +1,26 @@ +commit 6c9efc8219e35f4eb17e94b364f4c371cfb56cca +Author: Aurélien Nephtali <aurelien.nephtali@corp.ovh.com> +Date: Thu Apr 19 16:56:07 2018 +0200 + + BUG/MINOR: pattern: Add a missing HA_SPIN_INIT() in pat_ref_newid() + + pat_ref_newid() is lacking a spinlock init. It was probably forgotten + in b5997f740b ("MAJOR: threads/map: Make acls/maps thread safe"). + + Signed-off-by: Aurélien Nephtali <aurelien.nephtali@corp.ovh.com> + (cherry picked from commit 564d15a71ecb3ae3372767866335cfbc068c4b48) + Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> + +diff --git a/src/pattern.c b/src/pattern.c +index fe672f12..2eb82650 100644 +--- a/src/pattern.c ++++ b/src/pattern.c +@@ -1906,7 +1906,7 @@ struct pat_ref *pat_ref_newid(int unique_id, const char *display, unsigned int f + ref->unique_id = unique_id; + LIST_INIT(&ref->head); + LIST_INIT(&ref->pat); +- ++ HA_SPIN_INIT(&ref->lock); + LIST_ADDQ(&pattern_reference, &ref->list); + + return ref; diff --git a/net/haproxy/patches/0001-BUG-MAJOR-channel-Fix-crash-when-trying-to-read-from-a-closed-socket.patch b/net/haproxy/patches/0001-BUG-MAJOR-channel-Fix-crash-when-trying-to-read-from-a-closed-socket.patch new file mode 100644 index 000000000..606c3bf09 --- /dev/null +++ b/net/haproxy/patches/0001-BUG-MAJOR-channel-Fix-crash-when-trying-to-read-from-a-closed-socket.patch @@ -0,0 +1,87 @@ +commit e0f6d4a4e8696140d1fcff812fb287d534d702e9 +Author: Tim Duesterhus <tim@bastelstu.be> +Date: Tue Apr 24 19:20:43 2018 +0200 + + BUG/MAJOR: channel: Fix crash when trying to read from a closed socket + + When haproxy is compiled using GCC <= 3.x or >= 5.x the `unlikely` + macro performs a comparison with zero: `(x) != 0`, thus returning + either 0 or 1. + + In `int co_getline_nc()` this macro was accidentally applied to + the variable `retcode` itself, instead of the result of the + comparison `retcode <= 0`. As a result any negative `retcode` + is converted to `1` for purposes of the comparison. + Thus never taking the branch (and exiting the function) for + negative values. + + This in turn leads to reads of uninitialized memory in the for-loop + below: + + ==12141== Conditional jump or move depends on uninitialised value(s) + ==12141== at 0x4EB6B4: co_getline_nc (channel.c:346) + ==12141== by 0x421CA4: hlua_socket_receive_yield (hlua.c:1713) + ==12141== by 0x421F6F: hlua_socket_receive (hlua.c:1896) + ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529B497: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529711A: lua_pcallk (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x52ABDF0: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529A9F1: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529B523: lua_resume (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== + ==12141== Use of uninitialised value of size 8 + ==12141== at 0x4EB6B9: co_getline_nc (channel.c:346) + ==12141== by 0x421CA4: hlua_socket_receive_yield (hlua.c:1713) + ==12141== by 0x421F6F: hlua_socket_receive (hlua.c:1896) + ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529B497: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529711A: lua_pcallk (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x52ABDF0: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529A9F1: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529B523: lua_resume (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== + ==12141== Invalid read of size 1 + ==12141== at 0x4EB6B9: co_getline_nc (channel.c:346) + ==12141== by 0x421CA4: hlua_socket_receive_yield (hlua.c:1713) + ==12141== by 0x421F6F: hlua_socket_receive (hlua.c:1896) + ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529B497: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529711A: lua_pcallk (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x52ABDF0: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529B08F: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x52A7EFC: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529A9F1: ??? (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== by 0x529B523: lua_resume (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==12141== Address 0x8637171e928bb500 is not stack'd, malloc'd or (recently) free'd + + Fix this bug by correctly applying the `unlikely` macro to the result of the comparison. + + This bug exists as of commit ca16b038132444dea06e6d83953034128a812bce + which is the first commit adding this function. + + v1.6-dev1 is the first tag containing this commit, the fix should + be backported to haproxy 1.6 and newer. + + (cherry picked from commit 45be38c9c7ba2b20806f2b887876db4fb5b9457c) + Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> + +diff --git a/src/channel.c b/src/channel.c +index bd5c4de0..3770502c 100644 +--- a/src/channel.c ++++ b/src/channel.c +@@ -340,7 +340,7 @@ int co_getline_nc(const struct channel *chn, + int l; + + retcode = co_getblk_nc(chn, blk1, len1, blk2, len2); +- if (unlikely(retcode) <= 0) ++ if (unlikely(retcode <= 0)) + return retcode; + + for (l = 0; l < *len1 && (*blk1)[l] != '\n'; l++); diff --git a/net/haproxy/patches/0002-BUG-MINOR-log-t_idle-Ti-is-not-set-for-some-requests.patch b/net/haproxy/patches/0002-BUG-MINOR-log-t_idle-Ti-is-not-set-for-some-requests.patch new file mode 100644 index 000000000..9fffb9597 --- /dev/null +++ b/net/haproxy/patches/0002-BUG-MINOR-log-t_idle-Ti-is-not-set-for-some-requests.patch @@ -0,0 +1,50 @@ +commit 0e645ba57ddff9163a3d9b5626f189e974e671bd +Author: Rian McGuire <rian.mcguire@stileeducation.com> +Date: Tue Apr 24 11:19:21 2018 -0300 + + BUG/MINOR: log: t_idle (%Ti) is not set for some requests + + If TCP content inspection is used, msg_state can be >= HTTP_MSG_ERROR + the first time http_wait_for_request is called. t_idle was being left + unset in that case. + + In the example below : + stick-table type string len 64 size 100k expire 60s + tcp-request inspect-delay 1s + tcp-request content track-sc1 hdr(X-Session) + + %Ti will always be -1, because the msg_state is already at HTTP_MSG_BODY + when http_wait_for_request is called for the first time. + + This patch should backported to 1.8 and 1.7. + + (cherry picked from commit 89fcb7d929283e904cabad58de495d62fc753da2) + Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> + +diff --git a/src/proto_http.c b/src/proto_http.c +index b38dd84f..4c18a27c 100644 +--- a/src/proto_http.c ++++ b/src/proto_http.c +@@ -1618,18 +1618,16 @@ int http_wait_for_request(struct stream *s, struct channel *req, int an_bit) + /* we're speaking HTTP here, so let's speak HTTP to the client */ + s->srv_error = http_return_srv_error; + ++ /* If there is data available for analysis, log the end of the idle time. */ ++ if (buffer_not_empty(req->buf) && s->logs.t_idle == -1) ++ s->logs.t_idle = tv_ms_elapsed(&s->logs.tv_accept, &now) - s->logs.t_handshake; ++ + /* There's a protected area at the end of the buffer for rewriting + * purposes. We don't want to start to parse the request if the + * protected area is affected, because we may have to move processed + * data later, which is much more complicated. + */ + if (buffer_not_empty(req->buf) && msg->msg_state < HTTP_MSG_ERROR) { +- +- /* This point is executed when some data is avalaible for analysis, +- * so we log the end of the idle time. */ +- if (s->logs.t_idle == -1) +- s->logs.t_idle = tv_ms_elapsed(&s->logs.tv_accept, &now) - s->logs.t_handshake; +- + if (txn->flags & TX_NOT_FIRST) { + if (unlikely(!channel_is_rewritable(req))) { + if (req->flags & (CF_SHUTW|CF_SHUTW_NOW|CF_WRITE_ERROR|CF_WRITE_TIMEOUT)) diff --git a/net/haproxy/patches/0003-BUG-MEDIUM-lua-Fix-segmentation-fault-if-a-Lua-task-exits.patch b/net/haproxy/patches/0003-BUG-MEDIUM-lua-Fix-segmentation-fault-if-a-Lua-task-exits.patch new file mode 100644 index 000000000..2a52c7cbd --- /dev/null +++ b/net/haproxy/patches/0003-BUG-MEDIUM-lua-Fix-segmentation-fault-if-a-Lua-task-exits.patch @@ -0,0 +1,48 @@ +commit 17f3e16826e5b1a3f79b7421d69bb85be09a4ad9 +Author: Tim Duesterhus <tim@bastelstu.be> +Date: Tue Apr 24 13:56:01 2018 +0200 + + BUG/MEDIUM: lua: Fix segmentation fault if a Lua task exits + + PiBa-NL reported that haproxy crashes with a segmentation fault + if a function registered using `core.register_task` returns. + + An example Lua script that reproduces the bug is: + + mytask = function() + core.Info("Stopping task") + end + core.register_task(mytask) + + The Valgrind output is as follows: + + ==6759== Process terminating with default action of signal 11 (SIGSEGV) + ==6759== Access not within mapped region at address 0x20 + ==6759== at 0x5B60AA9: lua_sethook (in /usr/lib/x86_64-linux-gnu/liblua5.3.so.0.0.0) + ==6759== by 0x430264: hlua_ctx_resume (hlua.c:1009) + ==6759== by 0x43BB68: hlua_process_task (hlua.c:5525) + ==6759== by 0x4FED0A: process_runnable_tasks (task.c:231) + ==6759== by 0x4B2256: run_poll_loop (haproxy.c:2397) + ==6759== by 0x4B2256: run_thread_poll_loop (haproxy.c:2459) + ==6759== by 0x41A7E4: main (haproxy.c:3049) + + Add the missing `task = NULL` for the `HLUA_E_OK` case. The error cases + have been fixed as of 253e53e661c49fb9723535319cf511152bf09bc7 which + first was included in haproxy v1.8-dev3. This bugfix should be backported + to haproxy 1.8. + + (cherry picked from commit cd235c60425dbe66c9015a357369afacc4880211) + Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> + +diff --git a/src/hlua.c b/src/hlua.c +index 4e759c7c..d4b7ce91 100644 +--- a/src/hlua.c ++++ b/src/hlua.c +@@ -5528,6 +5528,7 @@ static struct task *hlua_process_task(struct task *task) + hlua_ctx_destroy(hlua); + task_delete(task); + task_free(task); ++ task = NULL; + break; + + case HLUA_E_AGAIN: /* co process or timeout wake me later. */ diff --git a/net/haproxy/patches/0004-MINOR-h2-detect-presence-of-CONNECT-and-or-content-length.patch b/net/haproxy/patches/0004-MINOR-h2-detect-presence-of-CONNECT-and-or-content-length.patch new file mode 100644 index 000000000..4369ff04a --- /dev/null +++ b/net/haproxy/patches/0004-MINOR-h2-detect-presence-of-CONNECT-and-or-content-length.patch @@ -0,0 +1,147 @@ +commit a8bcc7dd3fe5aa615f21e795375ff9225f004498 +Author: Willy Tarreau <w@1wt.eu> +Date: Wed Apr 25 18:13:58 2018 +0200 + + MINOR: h2: detect presence of CONNECT and/or content-length + + We'll need this in order to support uploading chunks. The h2 to h1 + converter checks for the presence of the content-length header field + as well as the CONNECT method and returns these information to the + caller. The caller indicates whether or not a body is detected for + the message (presence of END_STREAM or not). No transfer-encoding + header is emitted yet. + + (cherry picked from commit 174b06a572ef141f15d8b7ea64eb6b34ec4c9af1) + Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> + +diff --git a/include/common/h2.h b/include/common/h2.h +index 65c5ab1c..576ed105 100644 +--- a/include/common/h2.h ++++ b/include/common/h2.h +@@ -145,9 +145,15 @@ enum h2_err { + "\x0d\x0a\x53\x4d\x0d\x0a\x0d\x0a" + + ++/* some flags related to protocol parsing */ ++#define H2_MSGF_BODY 0x0001 // a body is present ++#define H2_MSGF_BODY_CL 0x0002 // content-length is present ++#define H2_MSGF_BODY_TUNNEL 0x0004 // a tunnel is in use (CONNECT) ++ ++ + /* various protocol processing functions */ + +-int h2_make_h1_request(struct http_hdr *list, char *out, int osize); ++int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int *msgf); + + /* + * Some helpful debugging functions. +diff --git a/src/h2.c b/src/h2.c +index 43ed7f3c..7d9ddd50 100644 +--- a/src/h2.c ++++ b/src/h2.c +@@ -36,9 +36,10 @@ + * stored in <phdr[]>. <fields> indicates what was found so far. This should be + * called once at the detection of the first general header field or at the end + * of the request if no general header field was found yet. Returns 0 on success +- * or a negative error code on failure. ++ * or a negative error code on failure. Upon success, <msgf> is updated with a ++ * few H2_MSGF_* flags indicating what was found while parsing. + */ +-static int h2_prepare_h1_reqline(uint32_t fields, struct ist *phdr, char **ptr, char *end) ++static int h2_prepare_h1_reqline(uint32_t fields, struct ist *phdr, char **ptr, char *end, unsigned int *msgf) + { + char *out = *ptr; + int uri_idx = H2_PHDR_IDX_PATH; +@@ -62,6 +63,7 @@ static int h2_prepare_h1_reqline(uint32_t fields, struct ist *phdr, char **ptr, + } + // otherwise OK ; let's use the authority instead of the URI + uri_idx = H2_PHDR_IDX_AUTH; ++ *msgf |= H2_MSGF_BODY_TUNNEL; + } + else if ((fields & (H2_PHDR_FND_METH|H2_PHDR_FND_SCHM|H2_PHDR_FND_PATH)) != + (H2_PHDR_FND_METH|H2_PHDR_FND_SCHM|H2_PHDR_FND_PATH)) { +@@ -113,6 +115,10 @@ static int h2_prepare_h1_reqline(uint32_t fields, struct ist *phdr, char **ptr, + * for a max of <osize> bytes, and the amount of bytes emitted is returned. In + * case of error, a negative error code is returned. + * ++ * Upon success, <msgf> is filled with a few H2_MSGF_* flags indicating what ++ * was found while parsing. The caller must set it to zero in or H2_MSGF_BODY ++ * if a body is detected (!ES). ++ * + * The headers list <list> must be composed of : + * - n.name != NULL, n.len > 0 : literal header name + * - n.name == NULL, n.len > 0 : indexed pseudo header name number <n.len> +@@ -124,7 +130,7 @@ static int h2_prepare_h1_reqline(uint32_t fields, struct ist *phdr, char **ptr, + * The Cookie header will be reassembled at the end, and for this, the <list> + * will be used to create a linked list, so its contents may be destroyed. + */ +-int h2_make_h1_request(struct http_hdr *list, char *out, int osize) ++int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int *msgf) + { + struct ist phdr_val[H2_PHDR_NUM_ENTRIES]; + char *out_end = out + osize; +@@ -176,7 +182,7 @@ int h2_make_h1_request(struct http_hdr *list, char *out, int osize) + /* regular header field in (name,value) */ + if (!(fields & H2_PHDR_FND_NONE)) { + /* no more pseudo-headers, time to build the request line */ +- ret = h2_prepare_h1_reqline(fields, phdr_val, &out, out_end); ++ ret = h2_prepare_h1_reqline(fields, phdr_val, &out, out_end, msgf); + if (ret != 0) + goto leave; + fields |= H2_PHDR_FND_NONE; +@@ -185,6 +191,10 @@ int h2_make_h1_request(struct http_hdr *list, char *out, int osize) + if (isteq(list[idx].n, ist("host"))) + fields |= H2_PHDR_FND_HOST; + ++ if ((*msgf & (H2_MSGF_BODY|H2_MSGF_BODY_TUNNEL|H2_MSGF_BODY_CL)) == H2_MSGF_BODY && ++ isteq(list[idx].n, ist("content-length"))) ++ *msgf |= H2_MSGF_BODY_CL; ++ + /* these ones are forbidden in requests (RFC7540#8.1.2.2) */ + if (isteq(list[idx].n, ist("connection")) || + isteq(list[idx].n, ist("proxy-connection")) || +@@ -232,7 +242,7 @@ int h2_make_h1_request(struct http_hdr *list, char *out, int osize) + + /* Let's dump the request now if not yet emitted. */ + if (!(fields & H2_PHDR_FND_NONE)) { +- ret = h2_prepare_h1_reqline(fields, phdr_val, &out, out_end); ++ ret = h2_prepare_h1_reqline(fields, phdr_val, &out, out_end, msgf); + if (ret != 0) + goto leave; + } +diff --git a/src/mux_h2.c b/src/mux_h2.c +index 4fde7fcc..82dd414a 100644 +--- a/src/mux_h2.c ++++ b/src/mux_h2.c +@@ -2626,6 +2626,7 @@ static int h2_frt_decode_headers(struct h2s *h2s, struct buffer *buf, int count) + struct chunk *tmp = get_trash_chunk(); + struct http_hdr list[MAX_HTTP_HDR * 2]; + struct chunk *copy = NULL; ++ unsigned int msgf; + int flen = h2c->dfl; + int outlen = 0; + int wrap; +@@ -2727,13 +2728,22 @@ static int h2_frt_decode_headers(struct h2s *h2s, struct buffer *buf, int count) + } + + /* OK now we have our header list in <list> */ +- outlen = h2_make_h1_request(list, bi_end(buf), try); ++ msgf = (h2c->dff & H2_F_DATA_END_STREAM) ? 0 : H2_MSGF_BODY; ++ outlen = h2_make_h1_request(list, bi_end(buf), try, &msgf); + + if (outlen < 0) { + h2c_error(h2c, H2_ERR_COMPRESSION_ERROR); + goto fail; + } + ++ if (msgf & H2_MSGF_BODY) { ++ /* a payload is present */ ++ if (msgf & H2_MSGF_BODY_CL) ++ h2s->flags |= H2_SF_DATA_CLEN; ++ else if (!(msgf & H2_MSGF_BODY_TUNNEL)) ++ h2s->flags |= H2_SF_DATA_CHNK; ++ } ++ + /* now consume the input data */ + bi_del(h2c->dbuf, h2c->dfl); + h2c->st0 = H2_CS_FRAME_H; diff --git a/net/haproxy/patches/0005-BUG-MEDIUM-h2-implement-missing-support-for-chunked-encoded-uploads.patch b/net/haproxy/patches/0005-BUG-MEDIUM-h2-implement-missing-support-for-chunked-encoded-uploads.patch new file mode 100644 index 000000000..f2dc3e6f3 --- /dev/null +++ b/net/haproxy/patches/0005-BUG-MEDIUM-h2-implement-missing-support-for-chunked-encoded-uploads.patch @@ -0,0 +1,164 @@ +commit 05657bd24ebaf20e5c508a435be9a0830591f033 +Author: Willy Tarreau <w@1wt.eu> +Date: Wed Apr 25 20:44:22 2018 +0200 + + BUG/MEDIUM: h2: implement missing support for chunked encoded uploads + + Upload requests not carrying a content-length nor tunnelling data must + be sent chunked-encoded over HTTP/1. The code was planned but for some + reason forgotten during the implementation, leading to such payloads to + be sent as tunnelled data. + + Browsers always emit a content length in uploads so this problem doesn't + happen for most sites. However some applications may send data frames + after a request without indicating it earlier. + + The only way to detect that a client will need to send data is that the + HEADERS frame doesn't hold the ES bit. In this case it's wise to look + for the content-length header. If it's not there, either we're in tunnel + (CONNECT method) or chunked-encoding (other methods). + + This patch implements this. + + The following request is sent using content-length : + + curl --http2 -sk https://127.0.0.1:4443/s2 -XPOST -T /large/file + + and these ones using chunked-encoding : + + curl --http2 -sk https://127.0.0.1:4443/s2 -XPUT -T /large/file + curl --http2 -sk https://127.0.0.1:4443/s2 -XPUT -T - < /dev/urandom + + Thanks to Robert Samuel Newson for raising this issue with details. + This fix must be backported to 1.8. + + (cherry picked from commit eba10f24b7da27cde60d2db24aeb1147e1657579) + Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> + +diff --git a/src/h2.c b/src/h2.c +index 7d9ddd50..5c83d6b6 100644 +--- a/src/h2.c ++++ b/src/h2.c +@@ -262,6 +262,14 @@ int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int + *(out++) = '\n'; + } + ++ if ((*msgf & (H2_MSGF_BODY|H2_MSGF_BODY_TUNNEL|H2_MSGF_BODY_CL)) == H2_MSGF_BODY) { ++ /* add chunked encoding */ ++ if (out + 28 > out_end) ++ goto fail; ++ memcpy(out, "transfer-encoding: chunked\r\n", 28); ++ out += 28; ++ } ++ + /* now we may have to build a cookie list. We'll dump the values of all + * visited headers. + */ +diff --git a/src/mux_h2.c b/src/mux_h2.c +index 82dd414a..5f1da0df 100644 +--- a/src/mux_h2.c ++++ b/src/mux_h2.c +@@ -2785,6 +2785,7 @@ static int h2_frt_transfer_data(struct h2s *h2s, struct buffer *buf, int count) + struct h2c *h2c = h2s->h2c; + int block1, block2; + unsigned int flen = h2c->dfl; ++ unsigned int chklen = 0; + + h2s->cs->flags &= ~CS_FL_RCV_MORE; + h2c->flags &= ~H2_CF_DEM_SFULL; +@@ -2820,14 +2821,35 @@ static int h2_frt_transfer_data(struct h2s *h2s, struct buffer *buf, int count) + return 0; + } + ++ /* chunked-encoding requires more room */ ++ if (h2s->flags & H2_SF_DATA_CHNK) { ++ chklen = MIN(flen, count); ++ chklen = (chklen < 16) ? 1 : (chklen < 256) ? 2 : ++ (chklen < 4096) ? 3 : (chklen < 65536) ? 4 : ++ (chklen < 1048576) ? 4 : 8; ++ chklen += 4; // CRLF, CRLF ++ } ++ + /* does it fit in output buffer or should we wait ? */ +- if (flen > count) { +- flen = count; +- if (!flen) { +- h2c->flags |= H2_CF_DEM_SFULL; +- h2s->cs->flags |= CS_FL_RCV_MORE; +- return 0; +- } ++ if (flen + chklen > count) { ++ if (chklen >= count) ++ goto full; ++ flen = count - chklen; ++ } ++ ++ if (h2s->flags & H2_SF_DATA_CHNK) { ++ /* emit the chunk size */ ++ unsigned int chksz = flen; ++ char str[10]; ++ char *beg; ++ ++ beg = str + sizeof(str); ++ *--beg = '\n'; ++ *--beg = '\r'; ++ do { ++ *--beg = hextab[chksz & 0xF]; ++ } while (chksz >>= 4); ++ bi_putblk(buf, beg, str + sizeof(str) - beg); + } + + /* Block1 is the length of the first block before the buffer wraps, +@@ -2844,6 +2866,11 @@ static int h2_frt_transfer_data(struct h2s *h2s, struct buffer *buf, int count) + if (block2) + bi_putblk(buf, b_ptr(h2c->dbuf, block1), block2); + ++ if (h2s->flags & H2_SF_DATA_CHNK) { ++ /* emit the CRLF */ ++ bi_putblk(buf, "\r\n", 2); ++ } ++ + /* now mark the input data as consumed (will be deleted from the buffer + * by the caller when seeing FRAME_A after sending the window update). + */ +@@ -2854,15 +2881,22 @@ static int h2_frt_transfer_data(struct h2s *h2s, struct buffer *buf, int count) + + if (h2c->dfl > h2c->dpl) { + /* more data available, transfer stalled on stream full */ +- h2c->flags |= H2_CF_DEM_SFULL; +- h2s->cs->flags |= CS_FL_RCV_MORE; +- return flen; ++ goto more; + } + + end_transfer: + /* here we're done with the frame, all the payload (except padding) was + * transferred. + */ ++ ++ if (h2c->dff & H2_F_DATA_END_STREAM && h2s->flags & H2_SF_DATA_CHNK) { ++ /* emit the trailing 0 CRLF CRLF */ ++ if (count < 5) ++ goto more; ++ chklen += 5; ++ bi_putblk(buf, "0\r\n\r\n", 5); ++ } ++ + h2c->rcvd_c += h2c->dpl; + h2c->rcvd_s += h2c->dpl; + h2c->dpl = 0; +@@ -2877,7 +2911,13 @@ static int h2_frt_transfer_data(struct h2s *h2s, struct buffer *buf, int count) + h2s->flags |= H2_SF_ES_RCVD; + } + +- return flen; ++ return flen + chklen; ++ full: ++ flen = chklen = 0; ++ more: ++ h2c->flags |= H2_CF_DEM_SFULL; ++ h2s->cs->flags |= CS_FL_RCV_MORE; ++ return flen + chklen; + } + + /* diff --git a/net/haproxy/patches/0006-BUG-MINOR-lua-threads-Make-luas-tasks-sticky-to-the-current-thread.patch b/net/haproxy/patches/0006-BUG-MINOR-lua-threads-Make-luas-tasks-sticky-to-the-current-thread.patch new file mode 100644 index 000000000..f6f9d55e5 --- /dev/null +++ b/net/haproxy/patches/0006-BUG-MINOR-lua-threads-Make-luas-tasks-sticky-to-the-current-thread.patch @@ -0,0 +1,36 @@ +commit 8b8d55be7e94ee3d758d41a21fa86a036e91a264 +Author: Christopher Faulet <cfaulet@haproxy.com> +Date: Wed Apr 25 10:34:45 2018 +0200 + + BUG/MINOR: lua/threads: Make lua's tasks sticky to the current thread + + PiBa-NL reported a bug with tasks registered in lua when HAProxy is started with + serveral threads. These tasks have not specific affinity with threads so they + can be woken up on any threads. So, it is impossbile for these tasks to handled + cosockets or applets, because cosockets and applets are sticky on the thread + which created them. It is forbbiden to manipulate a cosocket from another + thread. + + So to fix the bug, tasks registered in lua are now sticky to the current + thread. Because these tasks can be registered before threads creation, the + affinity is set the first time a lua's task is processed. + + This patch must be backported in HAProxy 1.8. + + (cherry picked from commit 5bc9972ed836517924eea91954d255d317a53418) + Signed-off-by: Christopher Faulet <cfaulet@haproxy.com> + +diff --git a/src/hlua.c b/src/hlua.c +index d4b7ce91..bd0b87e3 100644 +--- a/src/hlua.c ++++ b/src/hlua.c +@@ -5513,6 +5513,9 @@ static struct task *hlua_process_task(struct task *task) + struct hlua *hlua = task->context; + enum hlua_exec status; + ++ if (task->thread_mask == MAX_THREADS_MASK) ++ task_set_affinity(task, tid_bit); ++ + /* If it is the first call to the task, we must initialize the + * execution timeouts. + */