]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: hlua: don't use lua_tostring() from unprotected contexts
authorAurelien DARRAGON <adarragon@haproxy.com>
Fri, 1 Mar 2024 14:55:17 +0000 (15:55 +0100)
committerAurelien DARRAGON <adarragon@haproxy.com>
Mon, 4 Mar 2024 15:46:55 +0000 (16:46 +0100)
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]

src/hlua.c

index d8dcbf260a4469c0f8e4e5e8178e8f5ababc5703..fa0461186a8e8d3d9c47fb7d38861c8591e6e94b 100644 (file)
@@ -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 <index>
+ * 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 <index> is
+ * not removed from the stack.
+ *
+ * It is assumed that the calling function is allowed to manipulate <L>
+ */
+__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 <index>) 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 <index> 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);