From: William Lallemand Date: Thu, 20 Oct 2022 08:57:28 +0000 (+0200) Subject: BUG/MEDIUM: httpclient/lua: crash when the lua task timeout before the httpclient X-Git-Tag: v2.7-dev9~153 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bb581423b;p=thirdparty%2Fhaproxy.git 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 82204fe387..bc8c8df73d 100644 --- a/include/haproxy/hlua-t.h +++ b/include/haproxy/hlua-t.h @@ -111,6 +111,7 @@ struct hlua { 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 hlua_httpclient { 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 70be127f1a..9b646bc20c 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -1248,6 +1248,7 @@ int hlua_ctx_init(struct hlua *lua, int state_id, struct task *task, int already 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 @@ int hlua_ctx_init(struct hlua *lua, int state_id, struct task *task, int already 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 @@ void hlua_ctx_destroy(struct hlua *lua) 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 @@ __LJMP static int hlua_httpclient_gc(lua_State *L) 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 @@ __LJMP static int hlua_httpclient_new(lua_State *L) 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);