mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 10:37:35 +03:00
Fix lua stack overflow on recursive call to gsub()
The `zfs program` subcommand invokes a LUA interpreter to run ZFS "channel programs". This interpreter runs in a constrained environment, with defined memory limits. The LUA stack (used for LUA functions that call each other) is allocated in the kernel's heap, and is limited by the `-m MEMORY-LIMIT` flag and the `zfs_lua_max_memlimit` module parameter. The C stack is used by certain LUA features that are implemented in C. The C stack is limited by `LUAI_MAXCCALLS=20`, which limits call depth. Some LUA C calls use more stack space than others, and `gsub()` uses an unusually large amount. With a programming trick, it can be invoked recursively using the C stack (rather than the LUA stack). This overflows the 16KB Linux kernel stack after about 11 iterations, less than the limit of 20. One solution would be to decrease `LUAI_MAXCCALLS`. This could be made to work, but it has a few drawbacks: 1. The existing test suite does not pass with `LUAI_MAXCCALLS=10`. 2. There may be other LUA functions that use a lot of stack space, and the stack space may change depending on compiler version and options. This commit addresses the problem by adding a new limit on the amount of free space (in bytes) remaining on the C stack while running the LUA interpreter: `LUAI_MINCSTACK=4096`. If there is less than this amount of stack space remaining, a LUA runtime error is generated. Reviewed-by: George Wilson <gwilson@delphix.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Ryan Moeller <ryan@ixsystems.com> Reviewed-by: Allan Jude <allanjude@freebsd.org> Reviewed-by: Serapheim Dimitropoulos <serapheim@delphix.com> Signed-off-by: Matthew Ahrens <mahrens@delphix.com> Closes #10611 Closes #10613
This commit is contained in:
@@ -598,10 +598,12 @@ l_noret luaG_errormsg (lua_State *L) {
|
||||
|
||||
|
||||
l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
||||
L->runerror++;
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
addinfo(L, luaO_pushvfstring(L, fmt, argp));
|
||||
va_end(argp);
|
||||
luaG_errormsg(L);
|
||||
L->runerror--;
|
||||
}
|
||||
/* END CSTYLED */
|
||||
|
||||
+24
-1
@@ -29,6 +29,24 @@
|
||||
|
||||
|
||||
|
||||
/* Return the number of bytes available on the stack. */
|
||||
#if defined (_KERNEL) && defined(__linux__)
|
||||
#include <asm/current.h>
|
||||
static intptr_t stack_remaining(void) {
|
||||
char local;
|
||||
return (intptr_t)(&local - (char *)current->stack);
|
||||
}
|
||||
#elif defined (_KERNEL) && defined(__FreeBSD__)
|
||||
#include <sys/pcpu.h>
|
||||
static intptr_t stack_remaining(void) {
|
||||
char local;
|
||||
return (intptr_t)(&local - (char *)curthread->td_kstack);
|
||||
}
|
||||
#else
|
||||
static intptr_t stack_remaining(void) {
|
||||
return INTPTR_MAX;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
@@ -445,8 +463,13 @@ void luaD_call (lua_State *L, StkId func, int nResults, int allowyield) {
|
||||
if (L->nCcalls == LUAI_MAXCCALLS)
|
||||
luaG_runerror(L, "C stack overflow");
|
||||
else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
|
||||
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
|
||||
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
|
||||
}
|
||||
intptr_t remaining = stack_remaining();
|
||||
if (L->runerror == 0 && remaining < LUAI_MINCSTACK)
|
||||
luaG_runerror(L, "C stack overflow");
|
||||
if (L->runerror != 0 && remaining < LUAI_MINCSTACK / 2)
|
||||
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
|
||||
if (!allowyield) L->nny++;
|
||||
if (!luaD_precall(L, func, nResults)) /* is a Lua function? */
|
||||
luaV_execute(L); /* call it */
|
||||
|
||||
@@ -122,6 +122,12 @@ typedef LUAI_UACNUMBER l_uacNumber;
|
||||
#define LUAI_MAXCCALLS 20
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Minimum amount of available stack space (in bytes) to make a C call. With
|
||||
* gsub() recursion, the stack space between each luaD_call() is 1256 bytes.
|
||||
*/
|
||||
#define LUAI_MINCSTACK 4096
|
||||
|
||||
/*
|
||||
** maximum number of upvalues in a closure (both C and Lua). (Value
|
||||
** must fit in an unsigned char.)
|
||||
|
||||
@@ -214,6 +214,7 @@ static void preinit_state (lua_State *L, global_State *g) {
|
||||
L->nny = 1;
|
||||
L->status = LUA_OK;
|
||||
L->errfunc = 0;
|
||||
L->runerror = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -166,6 +166,7 @@ struct lua_State {
|
||||
unsigned short nCcalls; /* number of nested C calls */
|
||||
lu_byte hookmask;
|
||||
lu_byte allowhook;
|
||||
lu_byte runerror; /* handling a runtime error */
|
||||
int basehookcount;
|
||||
int hookcount;
|
||||
lua_Hook hook;
|
||||
|
||||
Reference in New Issue
Block a user