BUG/MEDIUM: httpclient/lua: crash when the lua task timeout before the httpclient
When the lua task finished before the httpclient that are associated to
it, there is a risk that the httpclient try to task_wakeup() the lua
task which does not exist anymore.
To fix this issue the httpclient used in a lua task are stored in a
list, and the httpclient are destroyed at the end of the lua task.
Must be backported in 2.5 and 2.6.
diff --git a/include/haproxy/hlua-t.h b/include/haproxy/hlua-t.h
index 82204fe..bc8c8df 100644
--- a/include/haproxy/hlua-t.h
+++ b/include/haproxy/hlua-t.h
@@ -111,6 +111,7 @@
struct task *task; /* The task associated with the lua stack execution.
We must wake this task to continue the task execution */
struct list com; /* The list head of the signals attached to this task. */
+ struct list hc_list; /* list of httpclient associated to this lua task */
struct ebpt_node node;
int gc_count; /* number of items which need a GC */
};
@@ -198,6 +199,7 @@
struct httpclient *hc; /* ptr to the httpclient instance */
size_t sent; /* payload sent */
luaL_Buffer b; /* buffer used to prepare strings. */
+ struct list by_hlua; /* linked in the current hlua task */
};
#else /* USE_LUA */
diff --git a/src/hlua.c b/src/hlua.c
index 70be127..9b646bc 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -1248,6 +1248,7 @@
lua->wake_time = TICK_ETERNITY;
lua->state_id = state_id;
LIST_INIT(&lua->com);
+ LIST_INIT(&lua->hc_list);
if (!already_safe) {
if (!SET_SAFE_LJMP_PARENT(lua)) {
lua->Tref = LUA_REFNIL;
@@ -1269,6 +1270,20 @@
return 1;
}
+/* kill all associated httpclient to this hlua task */
+static void hlua_httpclient_destroy_all(struct hlua *hlua)
+{
+ struct hlua_httpclient *hlua_hc, *back;
+
+ list_for_each_entry_safe(hlua_hc, back, &hlua->hc_list, by_hlua) {
+ if (hlua_hc->hc)
+ httpclient_stop_and_destroy(hlua_hc->hc);
+ hlua_hc->hc = NULL;
+ LIST_DELETE(&hlua_hc->by_hlua);
+ }
+}
+
+
/* Used to destroy the Lua coroutine when the attached stream or task
* is destroyed. The destroy also the memory context. The struct "lua"
* is not freed.
@@ -1281,6 +1296,9 @@
if (!lua->T)
goto end;
+ /* clean all running httpclient */
+ hlua_httpclient_destroy_all(lua);
+
/* Purge all the pending signals. */
notification_purge(&lua->com);
@@ -7012,7 +7030,10 @@
hlua_hc = MAY_LJMP(hlua_checkhttpclient(L, 1));
- httpclient_stop_and_destroy(hlua_hc->hc);
+ if (hlua_hc->hc)
+ httpclient_stop_and_destroy(hlua_hc->hc);
+
+ LIST_DELETE(&hlua_hc->by_hlua);
hlua_hc->hc = NULL;
@@ -7046,6 +7067,8 @@
if (!hlua_hc->hc)
goto err;
+ LIST_APPEND(&hlua->hc_list, &hlua_hc->by_hlua);
+
/* Pop a class stream metatable and affect it to the userdata. */
lua_rawgeti(L, LUA_REGISTRYINDEX, class_httpclient_ref);
lua_setmetatable(L, -2);