|
From bf583640ff69104e228b5f04e149b800ebc9e7d8 Mon Sep 17 00:00:00 2001
|
|
From: Thierry FOURNIER <thierry.fournier@ozon.io>
|
|
Date: Tue, 13 Dec 2016 13:06:23 +0100
|
|
Subject: [PATCH 01/19] BUG/MEDIUM: lua: In some case, the return of
|
|
sample-fetches is ignored (2)
|
|
|
|
This problem is already detected here:
|
|
|
|
8dc7316a6fa8cc6f3a60456376c8a13a6902a5be
|
|
|
|
Another case raises. Now HAProxy sends a final message (typically
|
|
with "http-request deny"). Once the the message is sent, the response
|
|
channel flags are not modified.
|
|
|
|
HAProxy executes a Lua sample-fecthes for building logs, and the
|
|
result is ignored because the response flag remains set to the value
|
|
HTTP_MSG_RPBEFORE. So the Lua function hlua_check_proto() want to
|
|
guarantee the valid state of the buffer and ask for aborting the
|
|
request.
|
|
|
|
The function check_proto() is not the good way to ensure request
|
|
consistency. The real question is not "Are the message valid ?", but
|
|
"Are the validity of message unchanged ?"
|
|
|
|
This patch memorize the parser state before entering int the Lua
|
|
code, and perform a check when it go out of the Lua code. If the parser
|
|
state change for down, the request is aborted because the HTTP message
|
|
is degraded.
|
|
|
|
This patch should be backported in version 1.6 and 1.7
|
|
(cherry picked from commit 11cfb3daecd789416103837001e30e9644b4c722)
|
|
---
|
|
include/types/hlua.h | 17 ++++++++++
|
|
src/hlua.c | 96 ++++++++++++++++++++++++++++++++++++----------------
|
|
2 files changed, 83 insertions(+), 30 deletions(-)
|
|
|
|
diff --git a/include/types/hlua.h b/include/types/hlua.h
|
|
index d2aaa4a..7a7c37f 100644
|
|
--- a/include/types/hlua.h
|
|
+++ b/include/types/hlua.h
|
|
@@ -49,6 +49,22 @@ enum hlua_exec {
|
|
HLUA_E_ERR, /* LUA stack execution failed without error message. */
|
|
};
|
|
|
|
+/* This struct is use for storing HAProxy parsers state
|
|
+ * before executing some Lua code. The goal is we can
|
|
+ * check and compare the parser state a the end of Lua
|
|
+ * execution. If the state is changed by Lua towards
|
|
+ * an unexpected state, we can abort the transaction.
|
|
+ */
|
|
+struct hlua_consistency {
|
|
+ enum pr_mode mode;
|
|
+ union {
|
|
+ struct {
|
|
+ int dir;
|
|
+ enum ht_state state;
|
|
+ } http;
|
|
+ } data;
|
|
+};
|
|
+
|
|
struct hlua {
|
|
lua_State *T; /* The LUA stack. */
|
|
int Tref; /* The reference of the stack in coroutine case.
|
|
@@ -65,6 +81,7 @@ struct hlua {
|
|
We must wake this task to continue the task execution */
|
|
struct list com; /* The list head of the signals attached to this task. */
|
|
struct ebpt_node node;
|
|
+ struct hlua_consistency cons; /* Store data consistency check. */
|
|
};
|
|
|
|
struct hlua_com {
|
|
diff --git a/src/hlua.c b/src/hlua.c
|
|
index 6889f2f..b91414b 100644
|
|
--- a/src/hlua.c
|
|
+++ b/src/hlua.c
|
|
@@ -2397,33 +2397,49 @@ static void hlua_resynchonize_proto(struct stream *stream, int dir)
|
|
}
|
|
}
|
|
|
|
-/* Check the protocole integrity after the Lua manipulations. Close the stream
|
|
- * and returns 0 if fails, otherwise returns 1. The direction is set using dir,
|
|
- * which equals either SMP_OPT_DIR_REQ or SMP_OPT_DIR_RES.
|
|
+/* This function is called before the Lua execution. It stores
|
|
+ * the differents parsers state before executing some Lua code.
|
|
*/
|
|
-static int hlua_check_proto(struct stream *stream, int dir)
|
|
+static inline void consistency_set(struct stream *stream, int opt, struct hlua_consistency *c)
|
|
+{
|
|
+ c->mode = stream->be->mode;
|
|
+ switch (c->mode) {
|
|
+ case PR_MODE_HTTP:
|
|
+ c->data.http.dir = opt & SMP_OPT_DIR;
|
|
+ if (c->data.http.dir == SMP_OPT_DIR_REQ)
|
|
+ c->data.http.state = stream->txn->req.msg_state;
|
|
+ else
|
|
+ c->data.http.state = stream->txn->rsp.msg_state;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* This function is called after the Lua execution. it
|
|
+ * returns true if the parser state is consistent, otherwise,
|
|
+ * it return false.
|
|
+ *
|
|
+ * In HTTP mode, the parser state must be in the same state
|
|
+ * or greater when we exit the function. Even if we do a
|
|
+ * control yield. This prevent to break the HTTP message
|
|
+ * from the Lua code.
|
|
+ */
|
|
+static inline int consistency_check(struct stream *stream, int opt, struct hlua_consistency *c)
|
|
{
|
|
- const struct chunk msg = { .len = 0 };
|
|
+ if (c->mode != stream->be->mode)
|
|
+ return 0;
|
|
|
|
- /* Protocol HTTP. The message parsing state must match the request or
|
|
- * response state. The problem that may happen is that Lua modifies
|
|
- * the request or response message *after* it was parsed, and corrupted
|
|
- * it so that it could not be processed anymore. We just need to verify
|
|
- * if the parser is still expected to run or not.
|
|
- */
|
|
- if (stream->be->mode == PR_MODE_HTTP) {
|
|
- if (dir == SMP_OPT_DIR_REQ &&
|
|
- !(stream->req.analysers & AN_REQ_WAIT_HTTP) &&
|
|
- stream->txn->req.msg_state < HTTP_MSG_ERROR) {
|
|
- stream_int_retnclose(&stream->si[0], &msg);
|
|
- return 0;
|
|
- }
|
|
- else if (dir == SMP_OPT_DIR_RES &&
|
|
- !(stream->res.analysers & AN_RES_WAIT_HTTP) &&
|
|
- stream->txn->rsp.msg_state < HTTP_MSG_ERROR) {
|
|
- stream_int_retnclose(&stream->si[0], &msg);
|
|
+ switch (c->mode) {
|
|
+ case PR_MODE_HTTP:
|
|
+ if (c->data.http.dir != (opt & SMP_OPT_DIR))
|
|
return 0;
|
|
- }
|
|
+ if (c->data.http.dir == SMP_OPT_DIR_REQ)
|
|
+ return stream->txn->req.msg_state >= c->data.http.state;
|
|
+ else
|
|
+ return stream->txn->rsp.msg_state >= c->data.http.state;
|
|
+ default:
|
|
+ return 1;
|
|
}
|
|
return 1;
|
|
}
|
|
@@ -5324,6 +5340,7 @@ static int hlua_sample_fetch_wrapper(const struct arg *arg_p, struct sample *smp
|
|
struct hlua_function *fcn = private;
|
|
struct stream *stream = smp->strm;
|
|
const char *error;
|
|
+ const struct chunk msg = { .len = 0 };
|
|
|
|
if (!stream)
|
|
return 0;
|
|
@@ -5338,6 +5355,8 @@ static int hlua_sample_fetch_wrapper(const struct arg *arg_p, struct sample *smp
|
|
return 0;
|
|
}
|
|
|
|
+ consistency_set(stream, smp->opt, &stream->hlua.cons);
|
|
+
|
|
/* If it is the first run, initialize the data for the call. */
|
|
if (!HLUA_IS_RUNNING(&stream->hlua)) {
|
|
|
|
@@ -5393,8 +5412,10 @@ static int hlua_sample_fetch_wrapper(const struct arg *arg_p, struct sample *smp
|
|
switch (hlua_ctx_resume(&stream->hlua, 0)) {
|
|
/* finished. */
|
|
case HLUA_E_OK:
|
|
- if (!hlua_check_proto(stream, (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES))
|
|
+ if (!consistency_check(stream, smp->opt, &stream->hlua.cons)) {
|
|
+ stream_int_retnclose(&stream->si[0], &msg);
|
|
return 0;
|
|
+ }
|
|
/* Convert the returned value in sample. */
|
|
hlua_lua2smp(stream->hlua.T, -1, smp);
|
|
lua_pop(stream->hlua.T, 1);
|
|
@@ -5405,13 +5426,15 @@ static int hlua_sample_fetch_wrapper(const struct arg *arg_p, struct sample *smp
|
|
|
|
/* yield. */
|
|
case HLUA_E_AGAIN:
|
|
- hlua_check_proto(stream, (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES);
|
|
+ if (!consistency_check(stream, smp->opt, &stream->hlua.cons))
|
|
+ stream_int_retnclose(&stream->si[0], &msg);
|
|
SEND_ERR(smp->px, "Lua sample-fetch '%s': cannot use yielded functions.\n", fcn->name);
|
|
return 0;
|
|
|
|
/* finished with error. */
|
|
case HLUA_E_ERRMSG:
|
|
- hlua_check_proto(stream, (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES);
|
|
+ if (!consistency_check(stream, smp->opt, &stream->hlua.cons))
|
|
+ stream_int_retnclose(&stream->si[0], &msg);
|
|
/* Display log. */
|
|
SEND_ERR(smp->px, "Lua sample-fetch '%s': %s.\n",
|
|
fcn->name, lua_tostring(stream->hlua.T, -1));
|
|
@@ -5419,7 +5442,8 @@ static int hlua_sample_fetch_wrapper(const struct arg *arg_p, struct sample *smp
|
|
return 0;
|
|
|
|
case HLUA_E_ERR:
|
|
- hlua_check_proto(stream, (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES);
|
|
+ if (!consistency_check(stream, smp->opt, &stream->hlua.cons))
|
|
+ stream_int_retnclose(&stream->si[0], &msg);
|
|
/* Display log. */
|
|
SEND_ERR(smp->px, "Lua sample-fetch '%s' returns an unknown error.\n", fcn->name);
|
|
|
|
@@ -5556,6 +5580,7 @@ static enum act_return hlua_action(struct act_rule *rule, struct proxy *px,
|
|
unsigned int analyzer;
|
|
int dir;
|
|
const char *error;
|
|
+ const struct chunk msg = { .len = 0 };
|
|
|
|
switch (rule->from) {
|
|
case ACT_F_TCP_REQ_CNT: analyzer = AN_REQ_INSPECT_FE ; dir = SMP_OPT_DIR_REQ; break;
|
|
@@ -5567,6 +5592,8 @@ static enum act_return hlua_action(struct act_rule *rule, struct proxy *px,
|
|
return ACT_RET_CONT;
|
|
}
|
|
|
|
+ consistency_set(s, dir, &s->hlua.cons);
|
|
+
|
|
/* In the execution wrappers linked with a stream, the
|
|
* Lua context can be not initialized. This behavior
|
|
* permits to save performances because a systematic
|
|
@@ -5635,8 +5662,10 @@ static enum act_return hlua_action(struct act_rule *rule, struct proxy *px,
|
|
switch (hlua_ctx_resume(&s->hlua, !(flags & ACT_FLAG_FINAL))) {
|
|
/* finished. */
|
|
case HLUA_E_OK:
|
|
- if (!hlua_check_proto(s, dir))
|
|
+ if (!consistency_check(s, dir, &s->hlua.cons)) {
|
|
+ stream_int_retnclose(&s->si[0], &msg);
|
|
return ACT_RET_ERR;
|
|
+ }
|
|
if (s->hlua.flags & HLUA_STOP)
|
|
return ACT_RET_STOP;
|
|
return ACT_RET_CONT;
|
|
@@ -5661,12 +5690,17 @@ static enum act_return hlua_action(struct act_rule *rule, struct proxy *px,
|
|
}
|
|
if (HLUA_IS_WAKEREQWR(&s->hlua))
|
|
s->req.flags |= CF_WAKE_WRITE;
|
|
+ /* We can quit the fcuntion without consistency check
|
|
+ * because HAProxy is not able to manipulate data, it
|
|
+ * is only allowed to call me again. */
|
|
return ACT_RET_YIELD;
|
|
|
|
/* finished with error. */
|
|
case HLUA_E_ERRMSG:
|
|
- if (!hlua_check_proto(s, dir))
|
|
+ if (!consistency_check(s, dir, &s->hlua.cons)) {
|
|
+ stream_int_retnclose(&s->si[0], &msg);
|
|
return ACT_RET_ERR;
|
|
+ }
|
|
/* Display log. */
|
|
SEND_ERR(px, "Lua function '%s': %s.\n",
|
|
rule->arg.hlua_rule->fcn.name, lua_tostring(s->hlua.T, -1));
|
|
@@ -5674,8 +5708,10 @@ static enum act_return hlua_action(struct act_rule *rule, struct proxy *px,
|
|
return ACT_RET_CONT;
|
|
|
|
case HLUA_E_ERR:
|
|
- if (!hlua_check_proto(s, dir))
|
|
+ if (!consistency_check(s, dir, &s->hlua.cons)) {
|
|
+ stream_int_retnclose(&s->si[0], &msg);
|
|
return ACT_RET_ERR;
|
|
+ }
|
|
/* Display log. */
|
|
SEND_ERR(px, "Lua function '%s' return an unknown error.\n",
|
|
rule->arg.hlua_rule->fcn.name);
|
|
--
|
|
2.10.2
|
|
|