From: Aurelien DARRAGON Date: Fri, 1 Mar 2024 14:55:17 +0000 (+0100) Subject: BUG/MINOR: hlua: don't use lua_tostring() from unprotected contexts X-Git-Tag: v3.0-dev5~36 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=369bfa0b50396a496a03fad59844196a3287784e;p=thirdparty%2Fhaproxy.git BUG/MINOR: hlua: don't use lua_tostring() from unprotected contexts As per lua documentation, lua_tostring() may raise a memory error. However, we're often using it to fetch the error message at the top of the stack (ie: after a failing lua call) from unprotected environments. In practise, lua_tostring() has rare chances of failing, but still, if it happens to be the case, it could crash the process and we better not risk it. So here, we add hlua_tostring_safe() function, which works exactly as lua_tostring(), but the function cannot LJMP as it will catch lua_tostring() exceptions to return NULL instead. Everywhere lua_tostring() was used to retrieve error string from such unprotected contexts, we now rely on hlua_tostring_safe(). This should be backported to all stable versions. [ada: ctx adj will be required, for versions prior to 2.8 event_hdl API didn't exist so some chunks won't apply, and prior to 2.5 filters API didn't exist either, so again, some chunks should be ignored] --- diff --git a/src/hlua.c b/src/hlua.c index d8dcbf260a..fa0461186a 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -208,6 +208,58 @@ static inline void hlua_unlock(struct hlua *hlua) lua_drop_global_lock(); } +/* below is an helper function to retrieve string on on Lua stack at + * in a safe way (function may not LJMP). It can be useful to retrieve errors + * at the top of the stack from an unprotected environment. + * + * The returned string will is only valid as long as the value at is + * not removed from the stack. + * + * It is assumed that the calling function is allowed to manipulate + */ +__LJMP static int _hlua_tostring_safe(lua_State *L) +{ + const char **str = lua_touserdata(L, 1); + const char *cur_str = MAY_LJMP(lua_tostring(L, 2)); + + if (cur_str) + *str = cur_str; + return 0; +} +static const char *hlua_tostring_safe(lua_State *L, int index) +{ + const char *str = NULL; + + if (!lua_checkstack(L, 4)) + return NULL; + + /* before any stack modification, save the targeted value on the top of + * the stack: this will allow us to use relative index to target it. + */ + lua_pushvalue(L, index); + + /* push our custom _hlua_tostring_safe() function on the stack, then push + * our own string pointer and targeted value (at ) as argument + */ + lua_pushcfunction(L, _hlua_tostring_safe); + lua_pushlightuserdata(L, &str); // 1st func argument = string pointer + lua_pushvalue(L, -3); // 2nd func argument = targeted value + + lua_remove(L, -4); // remove copy as we're done using it + + /* call our custom function with proper arguments using pcall() to catch + * exceptions (if any) + */ + switch (lua_pcall(L, 2, 0, 0)) { + case LUA_OK: + break; + default: + /* error was caught */ + return NULL; + } + return str; +} + #define SET_SAFE_LJMP_L(__L, __HLUA) \ ({ \ int ret; \ @@ -1820,7 +1872,7 @@ resume_execution: ret = HLUA_E_ERR; break; } - msg = lua_tostring(lua->T, -1); + msg = hlua_tostring_safe(lua->T, -1); trace = hlua_traceback(lua->T, ", "); if (msg) lua_pushfstring(lua->T, "[state-id %d] runtime error: %s from %s", lua->state_id, msg, trace); @@ -1845,7 +1897,7 @@ resume_execution: ret = HLUA_E_ERR; break; } - msg = lua_tostring(lua->T, -1); + msg = hlua_tostring_safe(lua->T, -1); if (msg) lua_pushfstring(lua->T, "[state-id %d] message handler error: %s", lua->state_id, msg); else @@ -8984,7 +9036,7 @@ struct task *hlua_process_task(struct task *task, void *context, unsigned int st SEND_ERR(NULL, "Lua task: execution timeout.\n"); goto err_task_abort; case HLUA_E_ERRMSG: - SEND_ERR(NULL, "Lua task: %s.\n", lua_tostring(hlua->T, -1)); + SEND_ERR(NULL, "Lua task: %s.\n", hlua_tostring_safe(hlua->T, -1)); goto err_task_abort; case HLUA_E_ERR: default: @@ -9259,7 +9311,7 @@ static void hlua_event_handler(struct hlua *hlua) break; case HLUA_E_ERRMSG: - SEND_ERR(NULL, "Lua event_hdl: %s.\n", lua_tostring(hlua->T, -1)); + SEND_ERR(NULL, "Lua event_hdl: %s.\n", hlua_tostring_safe(hlua->T, -1)); break; case HLUA_E_ERR: @@ -9598,7 +9650,7 @@ static struct task *hlua_event_runner(struct task *task, void *context, unsigned /* The following Lua calls can fail. */ if (!SET_SAFE_LJMP(hlua_sub->hlua)) { if (lua_type(hlua_sub->hlua->T, -1) == LUA_TSTRING) - error = lua_tostring(hlua_sub->hlua->T, -1); + error = hlua_tostring_safe(hlua_sub->hlua->T, -1); else error = "critical error"; ha_alert("Lua event_hdl: %s.\n", error); @@ -9893,7 +9945,7 @@ static int hlua_sample_conv_wrapper(const struct arg *arg_p, struct sample *smp, /* The following Lua calls can fail. */ if (!SET_SAFE_LJMP(stream->hlua)) { if (lua_type(stream->hlua->T, -1) == LUA_TSTRING) - error = lua_tostring(stream->hlua->T, -1); + error = hlua_tostring_safe(stream->hlua->T, -1); else error = "critical error"; SEND_ERR(stream->be, "Lua converter '%s': %s.\n", fcn->name, error); @@ -9965,7 +10017,7 @@ static int hlua_sample_conv_wrapper(const struct arg *arg_p, struct sample *smp, case HLUA_E_ERRMSG: /* Display log. */ SEND_ERR(stream->be, "Lua converter '%s': %s.\n", - fcn->name, lua_tostring(stream->hlua->T, -1)); + fcn->name, hlua_tostring_safe(stream->hlua->T, -1)); lua_pop(stream->hlua->T, 1); return 0; @@ -10018,7 +10070,7 @@ static int hlua_sample_fetch_wrapper(const struct arg *arg_p, struct sample *smp /* The following Lua calls can fail. */ if (!SET_SAFE_LJMP(stream->hlua)) { if (lua_type(stream->hlua->T, -1) == LUA_TSTRING) - error = lua_tostring(stream->hlua->T, -1); + error = hlua_tostring_safe(stream->hlua->T, -1); else error = "critical error"; SEND_ERR(smp->px, "Lua sample-fetch '%s': %s.\n", fcn->name, error); @@ -10091,7 +10143,7 @@ static int hlua_sample_fetch_wrapper(const struct arg *arg_p, struct sample *smp case HLUA_E_ERRMSG: /* Display log. */ SEND_ERR(smp->px, "Lua sample-fetch '%s': %s.\n", - fcn->name, lua_tostring(stream->hlua->T, -1)); + fcn->name, hlua_tostring_safe(stream->hlua->T, -1)); lua_pop(stream->hlua->T, 1); return 0; @@ -10348,7 +10400,7 @@ static enum act_return hlua_action(struct act_rule *rule, struct proxy *px, /* The following Lua calls can fail. */ if (!SET_SAFE_LJMP(s->hlua)) { if (lua_type(s->hlua->T, -1) == LUA_TSTRING) - error = lua_tostring(s->hlua->T, -1); + error = hlua_tostring_safe(s->hlua->T, -1); else error = "critical error"; SEND_ERR(px, "Lua function '%s': %s.\n", @@ -10442,7 +10494,7 @@ static enum act_return hlua_action(struct act_rule *rule, struct proxy *px, case HLUA_E_ERRMSG: /* Display log. */ SEND_ERR(px, "Lua function '%s': %s.\n", - rule->arg.hlua_rule->fcn->name, lua_tostring(s->hlua->T, -1)); + rule->arg.hlua_rule->fcn->name, hlua_tostring_safe(s->hlua->T, -1)); lua_pop(s->hlua->T, 1); goto end; @@ -10534,7 +10586,7 @@ static int hlua_applet_tcp_init(struct appctx *ctx) /* The following Lua calls can fail. */ if (!SET_SAFE_LJMP(hlua)) { if (lua_type(hlua->T, -1) == LUA_TSTRING) - error = lua_tostring(hlua->T, -1); + error = hlua_tostring_safe(hlua->T, -1); else error = "critical error"; SEND_ERR(strm->be, "Lua applet tcp '%s': %s.\n", @@ -10617,7 +10669,7 @@ void hlua_applet_tcp_fct(struct appctx *ctx) case HLUA_E_ERRMSG: /* Display log. */ SEND_ERR(px, "Lua applet tcp '%s': %s.\n", - rule->arg.hlua_rule->fcn->name, lua_tostring(hlua->T, -1)); + rule->arg.hlua_rule->fcn->name, hlua_tostring_safe(hlua->T, -1)); lua_pop(hlua->T, 1); goto error; @@ -10725,7 +10777,7 @@ static int hlua_applet_http_init(struct appctx *ctx) /* The following Lua calls can fail. */ if (!SET_SAFE_LJMP(hlua)) { if (lua_type(hlua->T, -1) == LUA_TSTRING) - error = lua_tostring(hlua->T, -1); + error = hlua_tostring_safe(hlua->T, -1); else error = "critical error"; SEND_ERR(strm->be, "Lua applet http '%s': %s.\n", @@ -10826,7 +10878,7 @@ void hlua_applet_http_fct(struct appctx *ctx) case HLUA_E_ERRMSG: /* Display log. */ SEND_ERR(px, "Lua applet http '%s': %s.\n", - rule->arg.hlua_rule->fcn->name, lua_tostring(hlua->T, -1)); + rule->arg.hlua_rule->fcn->name, hlua_tostring_safe(hlua->T, -1)); lua_pop(hlua->T, 1); goto error; @@ -11365,7 +11417,7 @@ static int hlua_cli_parse_fct(char **args, char *payload, struct appctx *appctx, /* The following Lua calls can fail. */ if (!SET_SAFE_LJMP(hlua)) { if (lua_type(hlua->T, -1) == LUA_TSTRING) - error = lua_tostring(hlua->T, -1); + error = hlua_tostring_safe(hlua->T, -1); else error = "critical error"; SEND_ERR(NULL, "Lua cli '%s': %s.\n", fcn->name, error); @@ -11450,7 +11502,7 @@ static int hlua_cli_io_handler_fct(struct appctx *appctx) case HLUA_E_ERRMSG: /* Display log. */ SEND_ERR(NULL, "Lua cli '%s': %s.\n", - fcn->name, lua_tostring(hlua->T, -1)); + fcn->name, hlua_tostring_safe(hlua->T, -1)); lua_pop(hlua->T, 1); return 1; @@ -11704,21 +11756,21 @@ static int hlua_filter_init_per_thread(struct proxy *px, struct flt_conf *fconf) conf->ref[state_id] = flt_ref; break; case LUA_ERRRUN: - ha_alert("Lua filter '%s' : runtime error : %s", conf->reg->name, lua_tostring(L, -1)); + ha_alert("Lua filter '%s' : runtime error : %s", conf->reg->name, hlua_tostring_safe(L, -1)); goto error; case LUA_ERRMEM: ha_alert("Lua filter '%s' : out of memory error", conf->reg->name); goto error; case LUA_ERRERR: - ha_alert("Lua filter '%s' : message handler error : %s", conf->reg->name, lua_tostring(L, -1)); + ha_alert("Lua filter '%s' : message handler error : %s", conf->reg->name, hlua_tostring_safe(L, -1)); goto error; #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 503 case LUA_ERRGCMM: - ha_alert("Lua filter '%s' : garbage collector error : %s", conf->reg->name, lua_tostring(L, -1)); + ha_alert("Lua filter '%s' : garbage collector error : %s", conf->reg->name, hlua_tostring_safe(L, -1)); goto error; #endif default: - ha_alert("Lua filter '%s' : unknown error : %s", conf->reg->name, lua_tostring(L, -1)); + ha_alert("Lua filter '%s' : unknown error : %s", conf->reg->name, hlua_tostring_safe(L, -1)); goto error; } @@ -11820,7 +11872,7 @@ static int hlua_filter_new(struct stream *s, struct filter *filter) const char *error; if (lua_type(s->hlua->T, -1) == LUA_TSTRING) - error = lua_tostring(s->hlua->T, -1); + error = hlua_tostring_safe(s->hlua->T, -1); else error = "critical error"; SEND_ERR(s->be, "Lua filter '%s': %s.\n", conf->reg->name, error); @@ -11875,7 +11927,7 @@ static int hlua_filter_new(struct stream *s, struct filter *filter) filter->ctx = flt_ctx; break; case HLUA_E_ERRMSG: - SEND_ERR(s->be, "Lua filter '%s' : %s.\n", conf->reg->name, lua_tostring(s->hlua->T, -1)); + SEND_ERR(s->be, "Lua filter '%s' : %s.\n", conf->reg->name, hlua_tostring_safe(s->hlua->T, -1)); ret = -1; goto end; case HLUA_E_ETMOUT: @@ -11953,7 +12005,7 @@ static int hlua_filter_callback(struct stream *s, struct filter *filter, const c const char *error; if (lua_type(flt_hlua->T, -1) == LUA_TSTRING) - error = lua_tostring(flt_hlua->T, -1); + error = hlua_tostring_safe(flt_hlua->T, -1); else error = "critical error"; SEND_ERR(s->be, "Lua filter '%s': %s.\n", conf->reg->name, error); @@ -12061,7 +12113,7 @@ static int hlua_filter_callback(struct stream *s, struct filter *filter, const c ret = 0; goto end; case HLUA_E_ERRMSG: - SEND_ERR(s->be, "Lua filter '%s' : %s.\n", conf->reg->name, lua_tostring(flt_hlua->T, -1)); + SEND_ERR(s->be, "Lua filter '%s' : %s.\n", conf->reg->name, hlua_tostring_safe(flt_hlua->T, -1)); ret = -1; goto end; case HLUA_E_ETMOUT: @@ -12550,7 +12602,7 @@ static int hlua_load_state(char **args, lua_State *L, char **err) /* Just load and compile the file. */ error = luaL_loadfile(L, args[0]); if (error) { - memprintf(err, "error in Lua file '%s': %s", args[0], lua_tostring(L, -1)); + memprintf(err, "error in Lua file '%s': %s", args[0], hlua_tostring_safe(L, -1)); lua_pop(L, 1); return -1; } @@ -12572,24 +12624,24 @@ static int hlua_load_state(char **args, lua_State *L, char **err) case LUA_OK: break; case LUA_ERRRUN: - memprintf(err, "Lua runtime error: %s", lua_tostring(L, -1)); + memprintf(err, "Lua runtime error: %s", hlua_tostring_safe(L, -1)); lua_pop(L, 1); return -1; case LUA_ERRMEM: memprintf(err, "Lua out of memory error"); return -1; case LUA_ERRERR: - memprintf(err, "Lua message handler error: %s", lua_tostring(L, -1)); + memprintf(err, "Lua message handler error: %s", hlua_tostring_safe(L, -1)); lua_pop(L, 1); return -1; #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 503 case LUA_ERRGCMM: - memprintf(err, "Lua garbage collector error: %s", lua_tostring(L, -1)); + memprintf(err, "Lua garbage collector error: %s", hlua_tostring_safe(L, -1)); lua_pop(L, 1); return -1; #endif default: - memprintf(err, "Lua unknown error: %s", lua_tostring(L, -1)); + memprintf(err, "Lua unknown error: %s", hlua_tostring_safe(L, -1)); lua_pop(L, 1); return -1; } @@ -12741,7 +12793,7 @@ static int hlua_config_prepend_path(char **args, int section_type, struct proxy if (setjmp(safe_ljmp_env) != 0) { lua_atpanic(L, hlua_panic_safe); if (lua_type(L, -1) == LUA_TSTRING) - error = lua_tostring(L, -1); + error = hlua_tostring_safe(L, -1); else error = "critical error"; fprintf(stderr, "lua-prepend-path: %s.\n", error); @@ -13010,7 +13062,7 @@ int hlua_post_init_state(lua_State *L) if (setjmp(safe_ljmp_env) != 0) { lua_atpanic(L, hlua_panic_safe); if (lua_type(L, -1) == LUA_TSTRING) - error = lua_tostring(L, -1); + error = hlua_tostring_safe(L, -1); else error = "critical error"; fprintf(stderr, "Lua post-init: %s.\n", error); @@ -13044,7 +13096,7 @@ int hlua_post_init_state(lua_State *L) case LUA_ERRRUN: if (!kind) kind = "runtime error"; - msg = lua_tostring(L, -1); + msg = hlua_tostring_safe(L, -1); trace = hlua_traceback(L, ", "); if (msg) ha_alert("Lua init: %s: '%s' from %s\n", kind, msg, trace); @@ -13305,7 +13357,7 @@ lua_State *hlua_init_state(int thread_num) if (setjmp(safe_ljmp_env) != 0) { lua_atpanic(L, hlua_panic_safe); if (lua_type(L, -1) == LUA_TSTRING) - error_msg = lua_tostring(L, -1); + error_msg = hlua_tostring_safe(L, -1); else error_msg = "critical error"; fprintf(stderr, "Lua init: %s.\n", error_msg);