BUG/MINOR: hlua: don't use lua_pushfstring() when we don't expect LJMP

lua_pushfstring() is used in multiple cleanup paths (upon error) to
push the error message that will be raised by lua_error(). However this
is often done from an unprotected environment, or in the middle of a
cleanup sequence, thus we don't want the function to LJMP! (it may cause
various issues ranging from memory leaks to crashing the process..)

Hopefully this has very few chances of happening but since the use of
lua_pushfstring() is limited to error reporting here, it's ok to use our
own hlua_pushfstring_safe() implementation with a little overhead to
ensure that the function will never LJMP.

This could be backported to all stable versions.

(cherry picked from commit c0a3c1281fac10ca7a590a4c34d102c8040e97a5)
Signed-off-by: Amaury Denoyelle <adenoyelle@haproxy.com>
This commit is contained in:
Aurelien DARRAGON 2024-06-03 23:18:24 +02:00 committed by Amaury Denoyelle
parent 1780d58964
commit 8e74276df7

View File

@ -260,6 +260,70 @@ static const char *hlua_tostring_safe(lua_State *L, int index)
return str;
}
/* below is an helper function similar to lua_pushvfstring() to push a
* formatted string on Lua stack but in a safe way (function may not LJMP).
* It can be useful to push allocated strings (ie: error messages) on the
* stack and ensure proper cleanup.
*
* Returns a pointer to the internal copy of the string on success and NULL
* on error.
*
* It is assumed that the calling function is allowed to manipulate <L>
*/
__LJMP static int _hlua_pushvfstring_safe(lua_State *L)
{
const char **dst = lua_touserdata(L, 1);
const char *fmt = lua_touserdata(L, 2);
va_list *argp = lua_touserdata(L, 3);
*dst = lua_pushvfstring(L, fmt, *argp);
return 1;
}
static const char *hlua_pushvfstring_safe(lua_State *L, const char *fmt, va_list argp)
{
const char *dst = NULL;
va_list cpy_argp; /* required if argp is implemented as array type */
if (!lua_checkstack(L, 4))
return NULL;
va_copy(cpy_argp, argp);
/* push our custom _hlua_pushvfstring_safe() function on the stack, then
* push our destination string pointer, fmt and arg list
*/
lua_pushcfunction(L, _hlua_pushvfstring_safe);
lua_pushlightuserdata(L, &dst); // 1st func argument = dst string pointer
lua_pushlightuserdata(L, (void *)fmt); // 2nd func argument = fmt
lua_pushlightuserdata(L, &cpy_argp); // 3rd func argument = arg list
/* call our custom function with proper arguments using pcall() to catch
* exceptions (if any)
*/
switch (lua_pcall(L, 3, 1, 0)) {
case LUA_OK:
break;
default:
/* error was caught */
dst = NULL;
}
va_end(cpy_argp);
return dst;
}
static const char *hlua_pushfstring_safe(lua_State *L, const char *fmt, ...)
{
va_list argp;
const char *dst;
va_start(argp, fmt);
dst = hlua_pushvfstring_safe(L, fmt, argp);
va_end(argp);
return dst;
}
#define SET_SAFE_LJMP_L(__L, __HLUA) \
({ \
int ret; \
@ -1367,8 +1431,8 @@ __LJMP int hlua_lua2arg_check(lua_State *L, int first, struct arg *argp,
}
reg = regex_comp(argp[idx].data.str.area, !(argp[idx].type_flags & ARGF_REG_ICASE), 1, &err);
if (!reg) {
msg = lua_pushfstring(L, "error compiling regex '%s' : '%s'",
argp[idx].data.str.area, err);
msg = hlua_pushfstring_safe(L, "error compiling regex '%s' : '%s'",
argp[idx].data.str.area, err);
free(err);
goto error;
}
@ -1388,7 +1452,8 @@ __LJMP int hlua_lua2arg_check(lua_State *L, int first, struct arg *argp,
ul = auth_find_userlist(argp[idx].data.str.area);
if (!ul) {
msg = lua_pushfstring(L, "unable to find userlist '%s'", argp[idx].data.str.area);
msg = hlua_pushfstring_safe(L, "unable to find userlist '%s'",
argp[idx].data.str.area);
goto error;
}
argp[idx].type = ARGT_USR;
@ -1412,9 +1477,9 @@ __LJMP int hlua_lua2arg_check(lua_State *L, int first, struct arg *argp,
/* Check for type of argument. */
if ((mask & ARGT_MASK) != argp[idx].type) {
msg = lua_pushfstring(L, "'%s' expected, got '%s'",
arg_type_names[(mask & ARGT_MASK)],
arg_type_names[argp[idx].type & ARGT_MASK]);
msg = hlua_pushfstring_safe(L, "'%s' expected, got '%s'",
arg_type_names[(mask & ARGT_MASK)],
arg_type_names[argp[idx].type & ARGT_MASK]);
goto error;
}
@ -1960,9 +2025,11 @@ resume_execution:
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);
hlua_pushfstring_safe(lua->T, "[state-id %d] runtime error: %s from %s",
lua->state_id, msg, trace);
else
lua_pushfstring(lua->T, "[state-id %d] unknown runtime error from %s", lua->state_id, trace);
hlua_pushfstring_safe(lua->T, "[state-id %d] unknown runtime error from %s",
lua->state_id, trace);
/* Move the error msg at the top and then empty the stack except last msg */
lua_insert(lua->T, -lua_gettop(lua->T));
@ -1984,9 +2051,11 @@ resume_execution:
}
msg = hlua_tostring_safe(lua->T, -1);
if (msg)
lua_pushfstring(lua->T, "[state-id %d] message handler error: %s", lua->state_id, msg);
hlua_pushfstring_safe(lua->T, "[state-id %d] message handler error: %s",
lua->state_id, msg);
else
lua_pushfstring(lua->T, "[state-id %d] message handler error", lua->state_id);
hlua_pushfstring_safe(lua->T, "[state-id %d] message handler error",
lua->state_id);
/* Move the error msg at the top and then empty the stack except last msg */
lua_insert(lua->T, -lua_gettop(lua->T));
@ -4700,7 +4769,7 @@ __LJMP static int hlua_run_sample_fetch(lua_State *L)
/* Run the special args checker. */
if (f->val_args && !f->val_args(args, NULL)) {
lua_pushfstring(L, "error in arguments");
hlua_pushfstring_safe(L, "error in arguments");
goto error;
}