- [PATCH 1/2] BUG/MEDIUM: stats: properly initialize the scope before - [PATCH 2/2] BUG/MEDIUM: http: don't forward client shutdown without - [PATCH 3/8] BUG/MINOR: check: fix tcpcheck error message - [PATCH 4/8] CLEANUP: checks: fix double usage of cur / current_step - [PATCH 5/8] BUG/MEDIUM: checks: do not dereference head of a - [PATCH 6/8] CLEANUP: checks: simplify the loop processing of - [PATCH 7/8] BUG/MAJOR: checks: always check for end of list before - [PATCH 8/8] BUG/MEDIUM: checks: do not dereference a list as a - [PATCH 09/10] BUG/MEDIUM: peers: apply a random reconnection timeout - [PATCH 10/10] DOC: Update doc about weight, act and bck fields in the - [PATCH 11/14] MINOR: ssl: add a destructor to free allocated SSL - [PATCH 12/14] BUG/MEDIUM: ssl: fix tune.ssl.default-dh-param value - [PATCH 13/14] BUG/MINOR: cfgparse: fix typo in 'option httplog' error - [PATCH 14/14] BUG/MEDIUM: cfgparse: segfault when userlist is misused Signed-off-by: heil <heil@terminal-consulting.de>lilik-openwrt-22.03
@ -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.0.5 | |||||
@ -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.0.5 | |||||
@ -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/8] 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.0.5 | |||||
@ -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/8] 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.0.5 | |||||
@ -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/8] 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.0.5 | |||||
@ -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/8] 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.0.5 | |||||
@ -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/8] 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.0.5 | |||||
@ -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/8] 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.0.5 | |||||
@ -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 09/10] 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.0.5 | |||||
@ -0,0 +1,33 @@ | |||||
From ac372e18c422841a9f1197b4238637c470e8edca Mon Sep 17 00:00:00 2001 | |||||
From: Pavlos Parissis <pavlos.parissis@gmail.com> | |||||
Date: Sat, 2 May 2015 20:30:44 +0200 | |||||
Subject: [PATCH 10/10] DOC: Update doc about weight, act and bck fields in the | |||||
statistics | |||||
Reorder description of the mentioned fields in order to match the | |||||
order of types | |||||
(cherry picked from commit 1f673c72c11d011bbd24e309d3155384eddf7a46) | |||||
--- | |||||
doc/configuration.txt | 6 +++--- | |||||
1 file changed, 3 insertions(+), 3 deletions(-) | |||||
diff --git a/doc/configuration.txt b/doc/configuration.txt | |||||
index a9d497e..6f5eeb1 100644 | |||||
--- a/doc/configuration.txt | |||||
+++ b/doc/configuration.txt | |||||
@@ -13240,9 +13240,9 @@ S (Servers). | |||||
server. The server value counts the number of times that server was | |||||
switched away from. | |||||
17. status [LFBS]: status (UP/DOWN/NOLB/MAINT/MAINT(via)...) | |||||
- 18. weight [..BS]: server weight (server), total weight (backend) | |||||
- 19. act [..BS]: server is active (server), number of active servers (backend) | |||||
- 20. bck [..BS]: server is backup (server), number of backup servers (backend) | |||||
+ 18. weight [..BS]: total weight (backend), server weight (server) | |||||
+ 19. act [..BS]: number of active servers (backend), server is active (server) | |||||
+ 20. bck [..BS]: number of backup servers (backend), server is backup (server) | |||||
21. chkfail [...S]: number of failed checks. (Only counts checks failed when | |||||
the server is up.) | |||||
22. chkdown [..BS]: number of UP->DOWN transitions. The backend counter counts | |||||
-- | |||||
2.0.5 | |||||
@ -0,0 +1,64 @@ | |||||
From 269a02fbb332da8faf6c2a614d45d5b5018816d1 Mon Sep 17 00:00:00 2001 | |||||
From: Remi Gacogne <rgacogne@aquaray.fr> | |||||
Date: Thu, 28 May 2015 16:39:47 +0200 | |||||
Subject: [PATCH 11/14] MINOR: ssl: add a destructor to free allocated SSL | |||||
ressources | |||||
Using valgrind or another memory leak tracking tool is easier | |||||
when the memory internally allocated by OpenSSL is cleanly released | |||||
at shutdown. | |||||
(cherry picked from commit d3a23c3eb8c0950d26204568a133207099923494) | |||||
--- | |||||
src/ssl_sock.c | 36 ++++++++++++++++++++++++++++++++++++ | |||||
1 file changed, 36 insertions(+) | |||||
diff --git a/src/ssl_sock.c b/src/ssl_sock.c | |||||
index d0f4d01..a78fc6a 100644 | |||||
--- a/src/ssl_sock.c | |||||
+++ b/src/ssl_sock.c | |||||
@@ -4717,6 +4717,42 @@ static void __ssl_sock_init(void) | |||||
cfg_register_keywords(&cfg_kws); | |||||
} | |||||
+__attribute__((destructor)) | |||||
+static void __ssl_sock_deinit(void) | |||||
+{ | |||||
+#ifndef OPENSSL_NO_DH | |||||
+ if (local_dh_1024) { | |||||
+ DH_free(local_dh_1024); | |||||
+ local_dh_1024 = NULL; | |||||
+ } | |||||
+ | |||||
+ if (local_dh_2048) { | |||||
+ DH_free(local_dh_2048); | |||||
+ local_dh_2048 = NULL; | |||||
+ } | |||||
+ | |||||
+ if (local_dh_4096) { | |||||
+ DH_free(local_dh_4096); | |||||
+ local_dh_4096 = NULL; | |||||
+ } | |||||
+ | |||||
+ if (local_dh_8192) { | |||||
+ DH_free(local_dh_8192); | |||||
+ local_dh_8192 = NULL; | |||||
+ } | |||||
+#endif | |||||
+ | |||||
+ ERR_remove_state(0); | |||||
+ ERR_free_strings(); | |||||
+ | |||||
+ EVP_cleanup(); | |||||
+ | |||||
+#if OPENSSL_VERSION_NUMBER >= 0x00907000L | |||||
+ CRYPTO_cleanup_all_ex_data(); | |||||
+#endif | |||||
+} | |||||
+ | |||||
+ | |||||
/* | |||||
* Local variables: | |||||
* c-indent-level: 8 | |||||
-- | |||||
2.0.5 | |||||
@ -0,0 +1,98 @@ | |||||
From 5d769ca828fdb055052b3dbc232864bdf2853c9f Mon Sep 17 00:00:00 2001 | |||||
From: Remi Gacogne <rgacogne@aquaray.fr> | |||||
Date: Thu, 28 May 2015 16:23:00 +0200 | |||||
Subject: [PATCH 12/14] BUG/MEDIUM: ssl: fix tune.ssl.default-dh-param value | |||||
being overwritten | |||||
MIME-Version: 1.0 | |||||
Content-Type: text/plain; charset=UTF-8 | |||||
Content-Transfer-Encoding: 8bit | |||||
Hervé Commowick reported that the logic used to avoid complaining about | |||||
ssl-default-dh-param not being set when static DH params are present | |||||
in the certificate file was clearly wrong when more than one sni_ctx | |||||
is used. | |||||
This patch stores whether static DH params are being used for each | |||||
SSL_CTX individually, and does not overwrite the value of | |||||
tune.ssl.default-dh-param. | |||||
(cherry picked from commit 4f902b88323927c9d25d391a809e3678ac31df41) | |||||
--- | |||||
src/ssl_sock.c | 28 +++++++++++++++++++++++----- | |||||
1 file changed, 23 insertions(+), 5 deletions(-) | |||||
diff --git a/src/ssl_sock.c b/src/ssl_sock.c | |||||
index a78fc6a..0f7819b 100644 | |||||
--- a/src/ssl_sock.c | |||||
+++ b/src/ssl_sock.c | |||||
@@ -47,6 +47,9 @@ | |||||
#ifdef SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB | |||||
#include <openssl/ocsp.h> | |||||
#endif | |||||
+#ifndef OPENSSL_NO_DH | |||||
+#include <openssl/dh.h> | |||||
+#endif | |||||
#include <common/buffer.h> | |||||
#include <common/compat.h> | |||||
@@ -107,6 +110,7 @@ int sslconns = 0; | |||||
int totalsslconns = 0; | |||||
#ifndef OPENSSL_NO_DH | |||||
+static int ssl_dh_ptr_index = -1; | |||||
static DH *local_dh_1024 = NULL; | |||||
static DH *local_dh_2048 = NULL; | |||||
static DH *local_dh_4096 = NULL; | |||||
@@ -1076,10 +1080,12 @@ int ssl_sock_load_dh_params(SSL_CTX *ctx, const char *file) | |||||
if (dh) { | |||||
ret = 1; | |||||
SSL_CTX_set_tmp_dh(ctx, dh); | |||||
- /* Setting ssl default dh param to the size of the static DH params | |||||
- found in the file. This way we know that there is no use | |||||
- complaining later about ssl-default-dh-param not being set. */ | |||||
- global.tune.ssl_default_dh_param = DH_size(dh) * 8; | |||||
+ | |||||
+ if (ssl_dh_ptr_index >= 0) { | |||||
+ /* store a pointer to the DH params to avoid complaining about | |||||
+ ssl-default-dh-param not being set for this SSL_CTX */ | |||||
+ SSL_CTX_set_ex_data(ctx, ssl_dh_ptr_index, dh); | |||||
+ } | |||||
} | |||||
else { | |||||
/* Clear openssl global errors stack */ | |||||
@@ -1274,6 +1280,12 @@ static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf | |||||
* the tree, so it will be discovered and cleaned in time. | |||||
*/ | |||||
#ifndef OPENSSL_NO_DH | |||||
+ /* store a NULL pointer to indicate we have not yet loaded | |||||
+ a custom DH param file */ | |||||
+ if (ssl_dh_ptr_index >= 0) { | |||||
+ SSL_CTX_set_ex_data(ctx, ssl_dh_ptr_index, NULL); | |||||
+ } | |||||
+ | |||||
ret = ssl_sock_load_dh_params(ctx, path); | |||||
if (ret < 0) { | |||||
if (err) | |||||
@@ -1593,7 +1605,9 @@ int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, SSL_CTX *ctx, struct proxy | |||||
/* If tune.ssl.default-dh-param has not been set and | |||||
no static DH params were in the certificate file. */ | |||||
- if (global.tune.ssl_default_dh_param == 0) { | |||||
+ if (global.tune.ssl_default_dh_param == 0 && | |||||
+ (ssl_dh_ptr_index == -1 || | |||||
+ SSL_CTX_get_ex_data(ctx, ssl_dh_ptr_index) == NULL)) { | |||||
ciphers = ctx->cipher_list; | |||||
if (ciphers) { | |||||
@@ -4715,6 +4729,10 @@ static void __ssl_sock_init(void) | |||||
bind_register_keywords(&bind_kws); | |||||
srv_register_keywords(&srv_kws); | |||||
cfg_register_keywords(&cfg_kws); | |||||
+ | |||||
+#ifndef OPENSSL_NO_DH | |||||
+ ssl_dh_ptr_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); | |||||
+#endif | |||||
} | |||||
__attribute__((destructor)) | |||||
-- | |||||
2.0.5 | |||||
@ -0,0 +1,29 @@ | |||||
From 629b1c000b26f0031246b9b529680b275a14118f Mon Sep 17 00:00:00 2001 | |||||
From: William Lallemand <wlallemand@haproxy.com> | |||||
Date: Thu, 28 May 2015 18:02:48 +0200 | |||||
Subject: [PATCH 13/14] BUG/MINOR: cfgparse: fix typo in 'option httplog' error | |||||
message | |||||
The error message was displaying the wrong argument when 'option | |||||
httplog' took a wrong argument. | |||||
(cherry picked from commit 77063bc0c6ceb4257c4e2c08411811ecc48be1aa) | |||||
--- | |||||
src/cfgparse.c | 2 +- | |||||
1 file changed, 1 insertion(+), 1 deletion(-) | |||||
diff --git a/src/cfgparse.c b/src/cfgparse.c | |||||
index e04eff8..3c3383d 100644 | |||||
--- a/src/cfgparse.c | |||||
+++ b/src/cfgparse.c | |||||
@@ -3792,7 +3792,7 @@ stats_error_parsing: | |||||
curproxy->options2 |= PR_O2_CLFLOG; | |||||
logformat = clf_http_log_format; | |||||
} else { | |||||
- Alert("parsing [%s:%d] : keyword '%s' only supports option 'clf'.\n", file, linenum, args[2]); | |||||
+ Alert("parsing [%s:%d] : keyword '%s' only supports option 'clf'.\n", file, linenum, args[1]); | |||||
err_code |= ERR_ALERT | ERR_FATAL; | |||||
goto out; | |||||
} | |||||
-- | |||||
2.0.5 | |||||
@ -0,0 +1,41 @@ | |||||
From faf3315f77c527e6e1d027deb7e853cdf6af5858 Mon Sep 17 00:00:00 2001 | |||||
From: William Lallemand <wlallemand@haproxy.com> | |||||
Date: Thu, 28 May 2015 18:03:51 +0200 | |||||
Subject: [PATCH 14/14] BUG/MEDIUM: cfgparse: segfault when userlist is misused | |||||
If the 'userlist' keyword parsing returns an error and no userlist were | |||||
previously created. The parsing of 'user' and 'group' leads to NULL | |||||
derefence. | |||||
The userlist pointer is now tested to prevent this issue. | |||||
(cherry picked from commit 4ac9f546120d42be8147e3d90588e7b9738af0cc) | |||||
--- | |||||
src/cfgparse.c | 5 +++++ | |||||
1 file changed, 5 insertions(+) | |||||
diff --git a/src/cfgparse.c b/src/cfgparse.c | |||||
index 3c3383d..392a78d 100644 | |||||
--- a/src/cfgparse.c | |||||
+++ b/src/cfgparse.c | |||||
@@ -5668,6 +5668,9 @@ cfg_parse_users(const char *file, int linenum, char **args, int kwm) | |||||
goto out; | |||||
} | |||||
+ if (!userlist) | |||||
+ goto out; | |||||
+ | |||||
for (ag = userlist->groups; ag; ag = ag->next) | |||||
if (!strcmp(ag->name, args[1])) { | |||||
Warning("parsing [%s:%d]: ignoring duplicated group '%s' in userlist '%s'.\n", | |||||
@@ -5718,6 +5721,8 @@ cfg_parse_users(const char *file, int linenum, char **args, int kwm) | |||||
err_code |= ERR_ALERT | ERR_FATAL; | |||||
goto out; | |||||
} | |||||
+ if (!userlist) | |||||
+ goto out; | |||||
for (newuser = userlist->users; newuser; newuser = newuser->next) | |||||
if (!strcmp(newuser->user, args[1])) { | |||||
-- | |||||
2.0.5 | |||||