forked from pool/haproxy
Accepting request 309522 from network:ha-clustering:Factory
1 OBS-URL: https://build.opensuse.org/request/show/309522 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/haproxy?expand=0&rev=29
This commit is contained in:
parent
4341af3d61
commit
bc564798e8
@ -0,0 +1,32 @@
|
||||
From 0aa5899911bbc765ba16ce52a80fa76230781779 Mon Sep 17 00:00:00 2001
|
||||
From: Willy Tarreau <w@1wt.eu>
|
||||
Date: Mon, 4 May 2015 18:07:56 +0200
|
||||
Subject: [PATCH 1/2] BUG/MEDIUM: stats: properly initialize the scope before
|
||||
dumping stats
|
||||
|
||||
Issuing a "show sess all" prior to a "show stat" on the CLI results in no
|
||||
proxy being dumped because the scope_len union member was not properly
|
||||
reinitialized.
|
||||
|
||||
This fix must be backported into 1.5.
|
||||
(cherry picked from commit 6bcb95da5b9cb143088102b460c7bcb37c1b3d81)
|
||||
---
|
||||
src/dumpstats.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/src/dumpstats.c b/src/dumpstats.c
|
||||
index b616478..ca084ac 100644
|
||||
--- a/src/dumpstats.c
|
||||
+++ b/src/dumpstats.c
|
||||
@@ -1109,6 +1109,8 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
|
||||
arg++;
|
||||
}
|
||||
|
||||
+ appctx->ctx.stats.scope_str = 0;
|
||||
+ appctx->ctx.stats.scope_len = 0;
|
||||
appctx->ctx.stats.flags = 0;
|
||||
if (strcmp(args[0], "show") == 0) {
|
||||
if (strcmp(args[1], "stat") == 0) {
|
||||
--
|
||||
2.1.4
|
||||
|
@ -0,0 +1,82 @@
|
||||
From 294e4676a3b775a7accb50eb8428f293c218b5e2 Mon Sep 17 00:00:00 2001
|
||||
From: Willy Tarreau <w@1wt.eu>
|
||||
Date: Mon, 11 May 2015 18:30:33 +0200
|
||||
Subject: [PATCH 2/2] BUG/MEDIUM: http: don't forward client shutdown without
|
||||
NOLINGER except for tunnels
|
||||
|
||||
There's an issue related with shutting down POST transfers or closing the
|
||||
connection after the end of the upload : the shutdown is forwarded to the
|
||||
server regardless of the abortonclose option. The problem it causes is that
|
||||
during a scan, brute force or whatever, it becomes possible that all source
|
||||
ports are exhausted with all sockets in TIME_WAIT state.
|
||||
|
||||
There are multiple issues at once in fact :
|
||||
- no action is done for the close, it automatically happens at the lower
|
||||
layers thanks for channel_auto_close(), so we cannot act on NOLINGER ;
|
||||
|
||||
- we *do* want to continue to send a clean shutdown in tunnel mode because
|
||||
some protocols transported over HTTP may need this, regardless of option
|
||||
abortonclose, thus we can't set the option inconditionally
|
||||
|
||||
- for all other modes, we do want to close the dirty way because we're
|
||||
certain whether we've sent everything or not, and we don't want to eat
|
||||
all source ports.
|
||||
|
||||
The solution is a bit complex and applies to DONE/TUNNEL states :
|
||||
|
||||
1) disable automatic close for everything not a tunnel and not just
|
||||
keep-alive / server-close. Force-close is now covered, as is HTTP/1.0
|
||||
which implicitly works in force-close mode ;
|
||||
|
||||
2) when processing option abortonclose, we know we can disable lingering
|
||||
if the client has closed and the connection is not in tunnel mode.
|
||||
|
||||
Since the last case above leads to a situation where the client side reports
|
||||
an error, we know the connection will not be reused, so leaving the flag on
|
||||
the stream-interface is safe. A client closing in the middle of the data
|
||||
transmission already aborts the transaction so this case is not a problem.
|
||||
|
||||
This fix must be backported to 1.5 where the problem was detected.
|
||||
(cherry picked from commit bbfb6c40854925367ae5f9e8b22c5c9a18dc69d5)
|
||||
---
|
||||
src/proto_http.c | 14 ++++++++++----
|
||||
1 file changed, 10 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/proto_http.c b/src/proto_http.c
|
||||
index 0ac3a47..5db64b5 100644
|
||||
--- a/src/proto_http.c
|
||||
+++ b/src/proto_http.c
|
||||
@@ -5452,9 +5452,10 @@ int http_request_forward_body(struct session *s, struct channel *req, int an_bit
|
||||
msg->sov -= msg->next;
|
||||
msg->next = 0;
|
||||
|
||||
- /* for keep-alive we don't want to forward closes on DONE */
|
||||
- if ((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_KAL ||
|
||||
- (txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL)
|
||||
+ /* we don't want to forward closes on DONE except in
|
||||
+ * tunnel mode.
|
||||
+ */
|
||||
+ if ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN)
|
||||
channel_dont_close(req);
|
||||
if (http_resync_states(s)) {
|
||||
/* some state changes occurred, maybe the analyser
|
||||
@@ -5478,10 +5479,15 @@ int http_request_forward_body(struct session *s, struct channel *req, int an_bit
|
||||
* want to monitor the client's connection and forward
|
||||
* any shutdown notification to the server, which will
|
||||
* decide whether to close or to go on processing the
|
||||
- * request.
|
||||
+ * request. We only do that in tunnel mode, and not in
|
||||
+ * other modes since it can be abused to exhaust source
|
||||
+ * ports.
|
||||
*/
|
||||
if (s->be->options & PR_O_ABRT_CLOSE) {
|
||||
channel_auto_read(req);
|
||||
+ if ((req->flags & (CF_SHUTR|CF_READ_NULL)) &&
|
||||
+ ((txn->flags & TX_CON_WANT_MSK) != TX_CON_WANT_TUN))
|
||||
+ s->si[1].flags |= SI_FL_NOLINGER;
|
||||
channel_auto_close(req);
|
||||
}
|
||||
else if (s->txn.meth == HTTP_METH_POST) {
|
||||
--
|
||||
2.1.4
|
||||
|
28
0003-BUG-MINOR-check-fix-tcpcheck-error-message.patch
Normal file
28
0003-BUG-MINOR-check-fix-tcpcheck-error-message.patch
Normal file
@ -0,0 +1,28 @@
|
||||
From 68e4fc2b9910dd090c5e729203b72444f75aaa75 Mon Sep 17 00:00:00 2001
|
||||
From: Baptiste Assmann <bedis9@gmail.com>
|
||||
Date: Fri, 1 May 2015 08:09:29 +0200
|
||||
Subject: [PATCH 3/9] BUG/MINOR: check: fix tcpcheck error message
|
||||
|
||||
add the keyword 'string' when required (error in a tcpcheck expect
|
||||
string)
|
||||
(cherry picked from commit 96a5c9b57738c05ecce7822093b9c4118123dc1e)
|
||||
---
|
||||
src/checks.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/checks.c b/src/checks.c
|
||||
index 71debb6..8b53f97 100644
|
||||
--- a/src/checks.c
|
||||
+++ b/src/checks.c
|
||||
@@ -614,7 +614,7 @@ static void chk_report_conn_err(struct connection *conn, int errno_bck, int expi
|
||||
}
|
||||
else if (check->last_started_step && check->last_started_step->action == TCPCHK_ACT_EXPECT) {
|
||||
if (check->last_started_step->string)
|
||||
- chunk_appendf(chk, " (string '%s')", check->last_started_step->string);
|
||||
+ chunk_appendf(chk, " (expect string '%s')", check->last_started_step->string);
|
||||
else if (check->last_started_step->expect_regex)
|
||||
chunk_appendf(chk, " (expect regex)");
|
||||
}
|
||||
--
|
||||
2.3.7
|
||||
|
178
0004-CLEANUP-checks-fix-double-usage-of-cur-current_step-.patch
Normal file
178
0004-CLEANUP-checks-fix-double-usage-of-cur-current_step-.patch
Normal file
@ -0,0 +1,178 @@
|
||||
From 4f889006269e4d3f802de46f280ed198a15e3a69 Mon Sep 17 00:00:00 2001
|
||||
From: Willy Tarreau <w@1wt.eu>
|
||||
Date: Wed, 13 May 2015 11:23:01 +0200
|
||||
Subject: [PATCH 4/9] CLEANUP: checks: fix double usage of cur / current_step
|
||||
in tcp-checks
|
||||
|
||||
This cleanup is a preliminary requirement to the upcoming fixes for
|
||||
the bug that affect tcp-check's improper use of lists. It will have
|
||||
to be backported to 1.5 though it will not easily apply.
|
||||
|
||||
There are two variables pointing to the current rule within the loop,
|
||||
and either one or the other is used depending on the code blocks,
|
||||
making it much harder to apply checks to fix the list walking bug.
|
||||
So first get rid of "cur" and only focus on current_step.
|
||||
(cherry picked from commit ce8c42a37a44a1e0cb94e81abb7cc2baf3d0ef80)
|
||||
|
||||
[wt: 1.5 doesn't have comments so this patch differs significantly
|
||||
from 1.6, but it's needed for the next batch of fixes]
|
||||
---
|
||||
src/checks.c | 57 ++++++++++++++++++++++++++++-----------------------------
|
||||
1 file changed, 28 insertions(+), 29 deletions(-)
|
||||
|
||||
diff --git a/src/checks.c b/src/checks.c
|
||||
index 8b53f97..cfdfe8c 100644
|
||||
--- a/src/checks.c
|
||||
+++ b/src/checks.c
|
||||
@@ -1859,7 +1859,7 @@ static int tcpcheck_get_step_id(struct server *s)
|
||||
static void tcpcheck_main(struct connection *conn)
|
||||
{
|
||||
char *contentptr;
|
||||
- struct tcpcheck_rule *cur, *next;
|
||||
+ struct tcpcheck_rule *next;
|
||||
int done = 0, ret = 0;
|
||||
struct check *check = conn->owner;
|
||||
struct server *s = check->server;
|
||||
@@ -1916,15 +1916,11 @@ static void tcpcheck_main(struct connection *conn)
|
||||
check->bo->o = 0;
|
||||
check->bi->p = check->bi->data;
|
||||
check->bi->i = 0;
|
||||
- cur = check->current_step = LIST_ELEM(head->n, struct tcpcheck_rule *, list);
|
||||
+ check->current_step = LIST_ELEM(head->n, struct tcpcheck_rule *, list);
|
||||
t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
|
||||
if (s->proxy->timeout.check)
|
||||
t->expire = tick_add_ifset(now_ms, s->proxy->timeout.check);
|
||||
}
|
||||
- /* keep on processing step */
|
||||
- else {
|
||||
- cur = check->current_step;
|
||||
- }
|
||||
|
||||
/* It's only the rules which will enable send/recv */
|
||||
__conn_data_stop_both(conn);
|
||||
@@ -1934,7 +1930,7 @@ static void tcpcheck_main(struct connection *conn)
|
||||
* or if we're about to send a string that does not fit in the remaining space.
|
||||
*/
|
||||
if (check->bo->o &&
|
||||
- (&cur->list == head ||
|
||||
+ (&check->current_step->list == head ||
|
||||
check->current_step->action != TCPCHK_ACT_SEND ||
|
||||
check->current_step->string_len >= buffer_total_space(check->bo))) {
|
||||
|
||||
@@ -1949,14 +1945,17 @@ static void tcpcheck_main(struct connection *conn)
|
||||
}
|
||||
|
||||
/* did we reach the end ? If so, let's check that everything was sent */
|
||||
- if (&cur->list == head) {
|
||||
+ if (&check->current_step->list == head) {
|
||||
if (check->bo->o)
|
||||
goto out_need_io;
|
||||
break;
|
||||
}
|
||||
|
||||
- /* have 'next' point to the next rule or NULL if we're on the last one */
|
||||
- next = (struct tcpcheck_rule *)cur->list.n;
|
||||
+ /* have 'next' point to the next rule or NULL if we're on the
|
||||
+ * last one, connect() needs this.
|
||||
+ */
|
||||
+ next = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
+
|
||||
if (&next->list == head)
|
||||
next = NULL;
|
||||
|
||||
@@ -2058,8 +2057,7 @@ static void tcpcheck_main(struct connection *conn)
|
||||
}
|
||||
|
||||
/* allow next rule */
|
||||
- cur = (struct tcpcheck_rule *)cur->list.n;
|
||||
- check->current_step = cur;
|
||||
+ check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
|
||||
/* don't do anything until the connection is established */
|
||||
if (!(conn->flags & CO_FL_CONNECTED)) {
|
||||
@@ -2113,8 +2111,7 @@ static void tcpcheck_main(struct connection *conn)
|
||||
*check->bo->p = '\0'; /* to make gdb output easier to read */
|
||||
|
||||
/* go to next rule and try to send */
|
||||
- cur = (struct tcpcheck_rule *)cur->list.n;
|
||||
- check->current_step = cur;
|
||||
+ check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
} /* end 'send' */
|
||||
else if (check->current_step->action == TCPCHK_ACT_EXPECT) {
|
||||
if (unlikely(check->result == CHK_RES_FAILED))
|
||||
@@ -2167,14 +2164,14 @@ static void tcpcheck_main(struct connection *conn)
|
||||
goto out_end_tcpcheck;
|
||||
}
|
||||
|
||||
- if (!done && (cur->string != NULL) && (check->bi->i < cur->string_len) )
|
||||
+ if (!done && (check->current_step->string != NULL) && (check->bi->i < check->current_step->string_len) )
|
||||
continue; /* try to read more */
|
||||
|
||||
tcpcheck_expect:
|
||||
- if (cur->string != NULL)
|
||||
- ret = my_memmem(contentptr, check->bi->i, cur->string, cur->string_len) != NULL;
|
||||
- else if (cur->expect_regex != NULL)
|
||||
- ret = regex_exec(cur->expect_regex, contentptr);
|
||||
+ if (check->current_step->string != NULL)
|
||||
+ ret = my_memmem(contentptr, check->bi->i, check->current_step->string, check->current_step->string_len) != NULL;
|
||||
+ else if (check->current_step->expect_regex != NULL)
|
||||
+ ret = regex_exec(check->current_step->expect_regex, contentptr);
|
||||
|
||||
if (!ret && !done)
|
||||
continue; /* try to read more */
|
||||
@@ -2182,11 +2179,11 @@ static void tcpcheck_main(struct connection *conn)
|
||||
/* matched */
|
||||
if (ret) {
|
||||
/* matched but we did not want to => ERROR */
|
||||
- if (cur->inverse) {
|
||||
+ if (check->current_step->inverse) {
|
||||
/* we were looking for a string */
|
||||
- if (cur->string != NULL) {
|
||||
+ if (check->current_step->string != NULL) {
|
||||
chunk_printf(&trash, "TCPCHK matched unwanted content '%s' at step %d",
|
||||
- cur->string, tcpcheck_get_step_id(s));
|
||||
+ check->current_step->string, tcpcheck_get_step_id(s));
|
||||
}
|
||||
else {
|
||||
/* we were looking for a regex */
|
||||
@@ -2198,8 +2195,9 @@ static void tcpcheck_main(struct connection *conn)
|
||||
}
|
||||
/* matched and was supposed to => OK, next step */
|
||||
else {
|
||||
- cur = (struct tcpcheck_rule*)cur->list.n;
|
||||
- check->current_step = cur;
|
||||
+ /* allow next rule */
|
||||
+ check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
+
|
||||
if (check->current_step->action == TCPCHK_ACT_EXPECT)
|
||||
goto tcpcheck_expect;
|
||||
__conn_data_stop_recv(conn);
|
||||
@@ -2208,9 +2206,10 @@ static void tcpcheck_main(struct connection *conn)
|
||||
else {
|
||||
/* not matched */
|
||||
/* not matched and was not supposed to => OK, next step */
|
||||
- if (cur->inverse) {
|
||||
- cur = (struct tcpcheck_rule*)cur->list.n;
|
||||
- check->current_step = cur;
|
||||
+ if (check->current_step->inverse) {
|
||||
+ /* allow next rule */
|
||||
+ check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
+
|
||||
if (check->current_step->action == TCPCHK_ACT_EXPECT)
|
||||
goto tcpcheck_expect;
|
||||
__conn_data_stop_recv(conn);
|
||||
@@ -2218,9 +2217,9 @@ static void tcpcheck_main(struct connection *conn)
|
||||
/* not matched but was supposed to => ERROR */
|
||||
else {
|
||||
/* we were looking for a string */
|
||||
- if (cur->string != NULL) {
|
||||
+ if (check->current_step->string != NULL) {
|
||||
chunk_printf(&trash, "TCPCHK did not match content '%s' at step %d",
|
||||
- cur->string, tcpcheck_get_step_id(s));
|
||||
+ check->current_step->string, tcpcheck_get_step_id(s));
|
||||
}
|
||||
else {
|
||||
/* we were looking for a regex */
|
||||
--
|
||||
2.3.7
|
||||
|
@ -0,0 +1,53 @@
|
||||
From b94a6d5a37499ce6649ad58f4a8c4664779abd8b Mon Sep 17 00:00:00 2001
|
||||
From: Willy Tarreau <w@1wt.eu>
|
||||
Date: Wed, 13 May 2015 11:38:17 +0200
|
||||
Subject: [PATCH 5/9] BUG/MEDIUM: checks: do not dereference head of a
|
||||
tcp-check at the end
|
||||
|
||||
When the end of the list is reached, the current step's action is checked
|
||||
to know if we must poll or not. Unfortunately, the main reason for going
|
||||
there is that we walked past the end of list and current_step points to
|
||||
the head. We cannot dereference ->action since it does not belong to this
|
||||
structure and can definitely crash if the address is not mapped.
|
||||
|
||||
This bug is unlikely to cause a crash since the action appears just after
|
||||
the list, and corresponds to the "char *check_req" pointer in the proxy
|
||||
struct, and it seems that we can't go there with current_step being null.
|
||||
At worst it can cause the check to register for recv events.
|
||||
|
||||
This fix needs to be backported to 1.5 since the code is incorrect there
|
||||
as well.
|
||||
(cherry picked from commit 53c5a049e1f4dbf67412472e23690dc6b3c8d0f8)
|
||||
---
|
||||
src/checks.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/checks.c b/src/checks.c
|
||||
index cfdfe8c..a887be1 100644
|
||||
--- a/src/checks.c
|
||||
+++ b/src/checks.c
|
||||
@@ -2237,10 +2237,12 @@ static void tcpcheck_main(struct connection *conn)
|
||||
goto out_end_tcpcheck;
|
||||
|
||||
out_need_io:
|
||||
+ /* warning, current_step may now point to the head */
|
||||
if (check->bo->o)
|
||||
__conn_data_want_send(conn);
|
||||
|
||||
- if (check->current_step->action == TCPCHK_ACT_EXPECT)
|
||||
+ if (&check->current_step->list != head &&
|
||||
+ check->current_step->action == TCPCHK_ACT_EXPECT)
|
||||
__conn_data_want_recv(conn);
|
||||
return;
|
||||
|
||||
@@ -2256,7 +2258,6 @@ static void tcpcheck_main(struct connection *conn)
|
||||
conn->flags |= CO_FL_ERROR;
|
||||
|
||||
__conn_data_stop_both(conn);
|
||||
-
|
||||
return;
|
||||
}
|
||||
|
||||
--
|
||||
2.3.7
|
||||
|
@ -0,0 +1,82 @@
|
||||
From ebb2bceb34d7787453548627ed0e99c60354672b Mon Sep 17 00:00:00 2001
|
||||
From: Willy Tarreau <w@1wt.eu>
|
||||
Date: Wed, 13 May 2015 11:59:14 +0200
|
||||
Subject: [PATCH 6/9] CLEANUP: checks: simplify the loop processing of
|
||||
tcp-checks
|
||||
|
||||
There is some unobvious redundancy between the various ways we can leave
|
||||
the loop. Some of them can be factored out. So now we leave the loop when
|
||||
we can't go further, whether it's caused by reaching the end of the rules
|
||||
or by a blocking I/O.
|
||||
(cherry picked from commit 263013d031d754c9f96de0d0cb5afcc011af6441)
|
||||
[wt: this patch is required for the next fix]
|
||||
---
|
||||
src/checks.c | 26 ++++++++++++++------------
|
||||
1 file changed, 14 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/src/checks.c b/src/checks.c
|
||||
index a887be1..a0c42f2 100644
|
||||
--- a/src/checks.c
|
||||
+++ b/src/checks.c
|
||||
@@ -1926,8 +1926,10 @@ static void tcpcheck_main(struct connection *conn)
|
||||
__conn_data_stop_both(conn);
|
||||
|
||||
while (1) {
|
||||
- /* we have to try to flush the output buffer before reading, at the end,
|
||||
- * or if we're about to send a string that does not fit in the remaining space.
|
||||
+ /* We have to try to flush the output buffer before reading, at
|
||||
+ * the end, or if we're about to send a string that does not fit
|
||||
+ * in the remaining space. That explains why we break out of the
|
||||
+ * loop after this control.
|
||||
*/
|
||||
if (check->bo->o &&
|
||||
(&check->current_step->list == head ||
|
||||
@@ -1940,16 +1942,12 @@ static void tcpcheck_main(struct connection *conn)
|
||||
__conn_data_stop_both(conn);
|
||||
goto out_end_tcpcheck;
|
||||
}
|
||||
- goto out_need_io;
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
|
||||
- /* did we reach the end ? If so, let's check that everything was sent */
|
||||
- if (&check->current_step->list == head) {
|
||||
- if (check->bo->o)
|
||||
- goto out_need_io;
|
||||
+ if (&check->current_step->list == head)
|
||||
break;
|
||||
- }
|
||||
|
||||
/* have 'next' point to the next rule or NULL if we're on the
|
||||
* last one, connect() needs this.
|
||||
@@ -2131,7 +2129,7 @@ static void tcpcheck_main(struct connection *conn)
|
||||
}
|
||||
}
|
||||
else
|
||||
- goto out_need_io;
|
||||
+ break;
|
||||
}
|
||||
|
||||
/* mark the step as started */
|
||||
@@ -2233,10 +2231,14 @@ static void tcpcheck_main(struct connection *conn)
|
||||
} /* end expect */
|
||||
} /* end loop over double chained step list */
|
||||
|
||||
- set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
|
||||
- goto out_end_tcpcheck;
|
||||
+ /* We're waiting for some I/O to complete, we've reached the end of the
|
||||
+ * rules, or both. Do what we have to do, otherwise we're done.
|
||||
+ */
|
||||
+ if (&check->current_step->list == head && !check->bo->o) {
|
||||
+ set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
|
||||
+ goto out_end_tcpcheck;
|
||||
+ }
|
||||
|
||||
- out_need_io:
|
||||
/* warning, current_step may now point to the head */
|
||||
if (check->bo->o)
|
||||
__conn_data_want_send(conn);
|
||||
--
|
||||
2.3.7
|
||||
|
@ -0,0 +1,90 @@
|
||||
From 97fccc87f1297d189ee80735e5b8746c34956eda Mon Sep 17 00:00:00 2001
|
||||
From: Willy Tarreau <w@1wt.eu>
|
||||
Date: Wed, 13 May 2015 12:08:21 +0200
|
||||
Subject: [PATCH 7/9] BUG/MAJOR: checks: always check for end of list before
|
||||
proceeding
|
||||
|
||||
This is the most important fix of this series. There's a risk of endless
|
||||
loop and crashes caused by the fact that we go past the head of the list
|
||||
when skipping to next rule, without checking if it's still a valid element.
|
||||
Most of the time, the ->action field is checked, which points to the proxy's
|
||||
check_req pointer (generally NULL), meaning the element is confused with a
|
||||
TCPCHK_ACT_SEND action.
|
||||
|
||||
The situation was accidently made worse with the addition of tcp-check
|
||||
comment since it also skips list elements. However, since the action that
|
||||
makes it go forward is TCPCHK_ACT_COMMENT (3), there's little chance to
|
||||
see this as a valid pointer, except on 64-bit machines where it can match
|
||||
the end of a check_req string pointer.
|
||||
|
||||
This fix heavily depends on previous cleanup and both must be backported
|
||||
to 1.5 where the bug is present.
|
||||
(cherry picked from commit f2c87353a7f8160930b5f342bb6d6ad0991ee3d1)
|
||||
[wt: this patch differs significantly from 1.6 since we don't have comments]
|
||||
---
|
||||
src/cfgparse.c | 4 +++-
|
||||
src/checks.c | 12 ++++++++++++
|
||||
2 files changed, 15 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/cfgparse.c b/src/cfgparse.c
|
||||
index 746c7eb..dba59d1 100644
|
||||
--- a/src/cfgparse.c
|
||||
+++ b/src/cfgparse.c
|
||||
@@ -4368,7 +4368,9 @@ stats_error_parsing:
|
||||
l = (struct list *)&curproxy->tcpcheck_rules;
|
||||
if (l->p != l->n) {
|
||||
tcpcheck = (struct tcpcheck_rule *)l->n;
|
||||
- if (tcpcheck && tcpcheck->action != TCPCHK_ACT_CONNECT) {
|
||||
+
|
||||
+ if (&tcpcheck->list != &curproxy->tcpcheck_rules
|
||||
+ && tcpcheck->action != TCPCHK_ACT_CONNECT) {
|
||||
Alert("parsing [%s:%d] : first step MUST also be a 'connect' when there is a 'connect' step in the tcp-check ruleset.\n",
|
||||
file, linenum);
|
||||
err_code |= ERR_ALERT | ERR_FATAL;
|
||||
diff --git a/src/checks.c b/src/checks.c
|
||||
index a0c42f2..e13d561 100644
|
||||
--- a/src/checks.c
|
||||
+++ b/src/checks.c
|
||||
@@ -2057,6 +2057,9 @@ static void tcpcheck_main(struct connection *conn)
|
||||
/* allow next rule */
|
||||
check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
|
||||
+ if (&check->current_step->list == head)
|
||||
+ break;
|
||||
+
|
||||
/* don't do anything until the connection is established */
|
||||
if (!(conn->flags & CO_FL_CONNECTED)) {
|
||||
/* update expire time, should be done by process_chk */
|
||||
@@ -2110,6 +2113,9 @@ static void tcpcheck_main(struct connection *conn)
|
||||
|
||||
/* go to next rule and try to send */
|
||||
check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
+
|
||||
+ if (&check->current_step->list == head)
|
||||
+ break;
|
||||
} /* end 'send' */
|
||||
else if (check->current_step->action == TCPCHK_ACT_EXPECT) {
|
||||
if (unlikely(check->result == CHK_RES_FAILED))
|
||||
@@ -2196,6 +2202,9 @@ static void tcpcheck_main(struct connection *conn)
|
||||
/* allow next rule */
|
||||
check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
|
||||
+ if (&check->current_step->list == head)
|
||||
+ break;
|
||||
+
|
||||
if (check->current_step->action == TCPCHK_ACT_EXPECT)
|
||||
goto tcpcheck_expect;
|
||||
__conn_data_stop_recv(conn);
|
||||
@@ -2208,6 +2217,9 @@ static void tcpcheck_main(struct connection *conn)
|
||||
/* allow next rule */
|
||||
check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
|
||||
+ if (&check->current_step->list == head)
|
||||
+ break;
|
||||
+
|
||||
if (check->current_step->action == TCPCHK_ACT_EXPECT)
|
||||
goto tcpcheck_expect;
|
||||
__conn_data_stop_recv(conn);
|
||||
--
|
||||
2.3.7
|
||||
|
116
0008-BUG-MEDIUM-checks-do-not-dereference-a-list-as-a-tcp.patch
Normal file
116
0008-BUG-MEDIUM-checks-do-not-dereference-a-list-as-a-tcp.patch
Normal file
@ -0,0 +1,116 @@
|
||||
From 5bff05986c501d9ffb67873b60472f9c2a2e41be Mon Sep 17 00:00:00 2001
|
||||
From: Willy Tarreau <w@1wt.eu>
|
||||
Date: Wed, 13 May 2015 12:24:53 +0200
|
||||
Subject: [PATCH 8/9] BUG/MEDIUM: checks: do not dereference a list as a
|
||||
tcpcheck struct
|
||||
|
||||
The method used to skip to next rule in the list is wrong, it assumes
|
||||
that the list element starts at the same offset as the rule. It happens
|
||||
to be true on most architectures since the list is the first element for
|
||||
now but it's definitely wrong. Now the code doesn't crash anymore when
|
||||
the struct list is moved anywhere else in the struct tcpcheck_rule.
|
||||
|
||||
This fix must be backported to 1.5.
|
||||
(cherry picked from commit 5581c27b579cbfc53afb0ca04cdeebe7e2200131)
|
||||
[wt: changes from 1.6 : no tcp-check comments, check becomes s->proxy]
|
||||
---
|
||||
src/cfgparse.c | 18 +++++++-----------
|
||||
src/checks.c | 15 +++++++++------
|
||||
2 files changed, 16 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/src/cfgparse.c b/src/cfgparse.c
|
||||
index dba59d1..e04eff8 100644
|
||||
--- a/src/cfgparse.c
|
||||
+++ b/src/cfgparse.c
|
||||
@@ -4362,20 +4362,16 @@ stats_error_parsing:
|
||||
const char *ptr_arg;
|
||||
int cur_arg;
|
||||
struct tcpcheck_rule *tcpcheck;
|
||||
- struct list *l;
|
||||
|
||||
/* check if first rule is also a 'connect' action */
|
||||
- l = (struct list *)&curproxy->tcpcheck_rules;
|
||||
- if (l->p != l->n) {
|
||||
- tcpcheck = (struct tcpcheck_rule *)l->n;
|
||||
+ tcpcheck = LIST_NEXT(&curproxy->tcpcheck_rules, struct tcpcheck_rule *, list);
|
||||
|
||||
- if (&tcpcheck->list != &curproxy->tcpcheck_rules
|
||||
- && tcpcheck->action != TCPCHK_ACT_CONNECT) {
|
||||
- Alert("parsing [%s:%d] : first step MUST also be a 'connect' when there is a 'connect' step in the tcp-check ruleset.\n",
|
||||
- file, linenum);
|
||||
- err_code |= ERR_ALERT | ERR_FATAL;
|
||||
- goto out;
|
||||
- }
|
||||
+ if (&tcpcheck->list != &curproxy->tcpcheck_rules
|
||||
+ && tcpcheck->action != TCPCHK_ACT_CONNECT) {
|
||||
+ Alert("parsing [%s:%d] : first step MUST also be a 'connect' when there is a 'connect' step in the tcp-check ruleset.\n",
|
||||
+ file, linenum);
|
||||
+ err_code |= ERR_ALERT | ERR_FATAL;
|
||||
+ goto out;
|
||||
}
|
||||
|
||||
cur_arg = 2;
|
||||
diff --git a/src/checks.c b/src/checks.c
|
||||
index e13d561..27a23b2 100644
|
||||
--- a/src/checks.c
|
||||
+++ b/src/checks.c
|
||||
@@ -1444,7 +1444,10 @@ static int connect_chk(struct task *t)
|
||||
quickack = check->type == 0 || check->type == PR_O2_TCPCHK_CHK;
|
||||
|
||||
if (check->type == PR_O2_TCPCHK_CHK && !LIST_ISEMPTY(&s->proxy->tcpcheck_rules)) {
|
||||
- struct tcpcheck_rule *r = (struct tcpcheck_rule *) s->proxy->tcpcheck_rules.n;
|
||||
+ struct tcpcheck_rule *r;
|
||||
+
|
||||
+ r = LIST_NEXT(&s->proxy->tcpcheck_rules, struct tcpcheck_rule *, list);
|
||||
+
|
||||
/* if first step is a 'connect', then tcpcheck_main must run it */
|
||||
if (r->action == TCPCHK_ACT_CONNECT) {
|
||||
tcpcheck_main(conn);
|
||||
@@ -1952,7 +1955,7 @@ static void tcpcheck_main(struct connection *conn)
|
||||
/* have 'next' point to the next rule or NULL if we're on the
|
||||
* last one, connect() needs this.
|
||||
*/
|
||||
- next = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
+ next = LIST_NEXT(&check->current_step->list, struct tcpcheck_rule *, list);
|
||||
|
||||
if (&next->list == head)
|
||||
next = NULL;
|
||||
@@ -2055,7 +2058,7 @@ static void tcpcheck_main(struct connection *conn)
|
||||
}
|
||||
|
||||
/* allow next rule */
|
||||
- check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
+ check->current_step = LIST_NEXT(&check->current_step->list, struct tcpcheck_rule *, list);
|
||||
|
||||
if (&check->current_step->list == head)
|
||||
break;
|
||||
@@ -2112,7 +2115,7 @@ static void tcpcheck_main(struct connection *conn)
|
||||
*check->bo->p = '\0'; /* to make gdb output easier to read */
|
||||
|
||||
/* go to next rule and try to send */
|
||||
- check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
+ check->current_step = LIST_NEXT(&check->current_step->list, struct tcpcheck_rule *, list);
|
||||
|
||||
if (&check->current_step->list == head)
|
||||
break;
|
||||
@@ -2200,7 +2203,7 @@ static void tcpcheck_main(struct connection *conn)
|
||||
/* matched and was supposed to => OK, next step */
|
||||
else {
|
||||
/* allow next rule */
|
||||
- check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
+ check->current_step = LIST_NEXT(&check->current_step->list, struct tcpcheck_rule *, list);
|
||||
|
||||
if (&check->current_step->list == head)
|
||||
break;
|
||||
@@ -2215,7 +2218,7 @@ static void tcpcheck_main(struct connection *conn)
|
||||
/* not matched and was not supposed to => OK, next step */
|
||||
if (check->current_step->inverse) {
|
||||
/* allow next rule */
|
||||
- check->current_step = (struct tcpcheck_rule *)check->current_step->list.n;
|
||||
+ check->current_step = LIST_NEXT(&check->current_step->list, struct tcpcheck_rule *, list);
|
||||
|
||||
if (&check->current_step->list == head)
|
||||
break;
|
||||
--
|
||||
2.3.7
|
||||
|
@ -0,0 +1,77 @@
|
||||
From 76a06b2804bcdba0fb2c19f834bdb511ce3cf344 Mon Sep 17 00:00:00 2001
|
||||
From: Willy Tarreau <w@1wt.eu>
|
||||
Date: Wed, 20 May 2015 10:39:04 +0200
|
||||
Subject: [PATCH 9/9] BUG/MEDIUM: peers: apply a random reconnection timeout
|
||||
|
||||
Commit 9ff95bb ("BUG/MEDIUM: peers: correctly configure the client timeout")
|
||||
uncovered an old bug in the peers : upon disconnect, we reconnect immediately.
|
||||
This sometimes results in both ends to do the same thing in parallel causing
|
||||
a loop of connect/accept/close/close that can last several seconds. The risk
|
||||
of occurrence of the trouble increases with latency, and is emphasized by the
|
||||
fact that idle connections are now frequently recycled (after 5s of idle).
|
||||
|
||||
In order to avoid this we must apply a random delay before reconnecting.
|
||||
Fortunately the mechanism already supports a reconnect delay, so here we
|
||||
compute the random timeout when killing a session. The delay is 50ms plus
|
||||
a random between 0 and 2 seconds. Ideally an exponential back-off would
|
||||
be preferred but it's preferable to keep the fix simple.
|
||||
|
||||
This bug was reported by Marco Corte.
|
||||
|
||||
This fix must be backported to 1.5 since the fix above was backported into
|
||||
1.5.12.
|
||||
(cherry picked from commit b4e34da692d8a7f6837ad16b3389f5830dbc11d2)
|
||||
---
|
||||
src/peers.c | 14 +++++++++++---
|
||||
1 file changed, 11 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/peers.c b/src/peers.c
|
||||
index b196d88..159f0a4 100644
|
||||
--- a/src/peers.c
|
||||
+++ b/src/peers.c
|
||||
@@ -1063,6 +1063,7 @@ static void peer_session_forceshutdown(struct session * session)
|
||||
{
|
||||
struct stream_interface *oldsi = NULL;
|
||||
struct appctx *appctx = NULL;
|
||||
+ struct peer_session *ps;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= 1; i++) {
|
||||
@@ -1079,6 +1080,14 @@ static void peer_session_forceshutdown(struct session * session)
|
||||
if (!appctx)
|
||||
return;
|
||||
|
||||
+ ps = (struct peer_session *)appctx->ctx.peers.ptr;
|
||||
+ /* we're killing a connection, we must apply a random delay before
|
||||
+ * retrying otherwise the other end will do the same and we can loop
|
||||
+ * for a while.
|
||||
+ */
|
||||
+ if (ps)
|
||||
+ ps->reconnect = tick_add(now_ms, MS_TO_TICKS(50 + random() % 2000));
|
||||
+
|
||||
/* call release to reinit resync states if needed */
|
||||
peer_session_release(oldsi);
|
||||
appctx->st0 = PEER_SESS_ST_END;
|
||||
@@ -1352,8 +1361,8 @@ static struct task *process_peer_sync(struct task * task)
|
||||
if (!ps->session) {
|
||||
/* no active session */
|
||||
if (ps->statuscode == 0 ||
|
||||
- ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
|
||||
((ps->statuscode == PEER_SESS_SC_CONNECTCODE ||
|
||||
+ ps->statuscode == PEER_SESS_SC_SUCCESSCODE ||
|
||||
ps->statuscode == PEER_SESS_SC_CONNECTEDCODE) &&
|
||||
tick_is_expired(ps->reconnect, now_ms))) {
|
||||
/* connection never tried
|
||||
@@ -1364,8 +1373,7 @@ static struct task *process_peer_sync(struct task * task)
|
||||
/* retry a connect */
|
||||
ps->session = peer_session_create(ps->peer, ps);
|
||||
}
|
||||
- else if (ps->statuscode == PEER_SESS_SC_CONNECTCODE ||
|
||||
- ps->statuscode == PEER_SESS_SC_CONNECTEDCODE) {
|
||||
+ else if (!tick_is_expired(ps->reconnect, now_ms)) {
|
||||
/* If previous session failed during connection
|
||||
* but reconnection timer is not expired */
|
||||
|
||||
--
|
||||
2.3.7
|
||||
|
@ -1,3 +1,34 @@
|
||||
-------------------------------------------------------------------
|
||||
Mon May 25 09:34:58 UTC 2015 - kgronlund@suse.com
|
||||
|
||||
- BUG/MINOR: check: fix tcpcheck error message
|
||||
- CLEANUP: checks: fix double usage of cur / current_step in tcp-checks
|
||||
- BUG/MEDIUM: checks: do not dereference head of a tcp-check at the end
|
||||
- CLEANUP: checks: simplify the loop processing of tcp-checks
|
||||
- BUG/MAJOR: checks: always check for end of list before proceeding
|
||||
- BUG/MEDIUM: checks: do not dereference a list as a tcpcheck struct
|
||||
- BUG/MEDIUM: peers: apply a random reconnection timeout
|
||||
- Add 0003-BUG-MINOR-check-fix-tcpcheck-error-message.patch
|
||||
- Add 0004-CLEANUP-checks-fix-double-usage-of-cur-current_step-.patch
|
||||
- Add 0005-BUG-MEDIUM-checks-do-not-dereference-head-of-a-tcp-c.patch
|
||||
- Add 0006-CLEANUP-checks-simplify-the-loop-processing-of-tcp-c.patch
|
||||
- Add 0007-BUG-MAJOR-checks-always-check-for-end-of-list-before.patch
|
||||
- Add 0008-BUG-MEDIUM-checks-do-not-dereference-a-list-as-a-tcp.patch
|
||||
- Add 0009-BUG-MEDIUM-peers-apply-a-random-reconnection-timeout.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon May 11 19:27:33 UTC 2015 - mrueckert@suse.de
|
||||
|
||||
- added 0002-BUG-MEDIUM-http-don-t-forward-client-shutdown-withou.patch
|
||||
BUG/MEDIUM: http: don't forward client shutdown without NOLINGER
|
||||
except for tunnels
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Mon May 4 22:02:30 UTC 2015 - mrueckert@suse.de
|
||||
|
||||
- added first patch from the 1.5 branch after the update:
|
||||
0001-BUG-MEDIUM-stats-properly-initialize-the-scope-befor.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Sat May 2 22:17:57 UTC 2015 - mrueckert@suse.de
|
||||
|
||||
|
26
haproxy.spec
26
haproxy.spec
@ -61,6 +61,23 @@ Patch1: haproxy-1.2.16_config_haproxy_user.patch
|
||||
Patch2: haproxy-makefile_lib.patch
|
||||
Patch3: sec-options.patch
|
||||
Patch4: haproxy-1.5.8-fix-bashisms.patch
|
||||
Patch5: 0001-BUG-MEDIUM-stats-properly-initialize-the-scope-befor.patch
|
||||
Patch6: 0002-BUG-MEDIUM-http-don-t-forward-client-shutdown-withou.patch
|
||||
# PATCH-FIX-UPSTREAM: BUG/MINOR: check: fix tcpcheck error message
|
||||
Patch7: 0003-BUG-MINOR-check-fix-tcpcheck-error-message.patch
|
||||
# PATCH-FIX-UPSTREAM: CLEANUP: checks: fix double usage of cur / current_step in tcp-checks
|
||||
Patch8: 0004-CLEANUP-checks-fix-double-usage-of-cur-current_step-.patch
|
||||
# PATCH-FIX-UPSTREAM: BUG/MEDIUM: checks: do not dereference head of a tcp-check at the end
|
||||
Patch9: 0005-BUG-MEDIUM-checks-do-not-dereference-head-of-a-tcp-c.patch
|
||||
# PATCH-FIX-UPSTREAM: CLEANUP: checks: simplify the loop processing of tcp-checks
|
||||
Patch10: 0006-CLEANUP-checks-simplify-the-loop-processing-of-tcp-c.patch
|
||||
# PATCH-FIX-UPSTREAM: BUG/MAJOR: checks: always check for end of list before proceeding
|
||||
Patch11: 0007-BUG-MAJOR-checks-always-check-for-end-of-list-before.patch
|
||||
# PATCH-FIX-UPSTREAM: BUG/MEDIUM: checks: do not dereference a list as a tcpcheck struct
|
||||
Patch12: 0008-BUG-MEDIUM-checks-do-not-dereference-a-list-as-a-tcp.patch
|
||||
# PATCH-FIX-UPSTREAM: BUG/MEDIUM: peers: apply a random reconnection timeout
|
||||
Patch13: 0009-BUG-MEDIUM-peers-apply-a-random-reconnection-timeout.patch
|
||||
|
||||
#
|
||||
Source99: haproxy-rpmlintrc
|
||||
#
|
||||
@ -95,6 +112,15 @@ the most work done from every CPU cycle.
|
||||
%patch2
|
||||
%patch3
|
||||
%patch4 -p1
|
||||
%patch5 -p1
|
||||
%patch6 -p1
|
||||
%patch7 -p1
|
||||
%patch8 -p1
|
||||
%patch9 -p1
|
||||
%patch10 -p1
|
||||
%patch11 -p1
|
||||
%patch12 -p1
|
||||
%patch13 -p1
|
||||
|
||||
%build
|
||||
%{__make} \
|
||||
|
Loading…
Reference in New Issue
Block a user