-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
-