From cd4de92762434e6ed0e6c207d56d365300396dd8 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 16 Jul 2024 11:33:30 -0300 Subject: [PATCH] Maximum stack size may not fit in unsigned short Therefore, fields ftransfer/ntransfer in lua_Debug must have type 'int'. (Maximum stack size must fit in an 'int'.) Also, this commit adds check that maximum stack size respects size_t for size in bytes. --- ldo.c | 43 +++++++++++++++++++++++++++++-------------- lstate.h | 4 ++-- lua.h | 4 ++-- luaconf.h | 8 ++++---- manual/manual.of | 15 +++++++++------ 5 files changed, 46 insertions(+), 28 deletions(-) diff --git a/ldo.c b/ldo.c index cd6dded6..34101ba3 100644 --- a/ldo.c +++ b/ldo.c @@ -171,6 +171,24 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { ** =================================================================== */ +/* some stack space for error handling */ +#define STACKERRSPACE 200 + + +/* maximum stack size that respects size_t */ +#define MAXSTACK_BYSIZET ((MAX_SIZET / sizeof(StackValue)) - STACKERRSPACE) + +/* +** Minimum between LUAI_MAXSTACK and MAXSTACK_BYSIZET +** (Maximum size for the stack must respect size_t.) +*/ +#define MAXSTACK cast_int(LUAI_MAXSTACK < MAXSTACK_BYSIZET \ + ? LUAI_MAXSTACK : MAXSTACK_BYSIZET) + + +/* stack size with extra space for error handling */ +#define ERRORSTACKSIZE (MAXSTACK + STACKERRSPACE) + /* ** Change all pointers to the stack into offsets. @@ -208,9 +226,6 @@ static void correctstack (lua_State *L) { } -/* some space for error handling */ -#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) - /* ** Reallocate the stack to a new size, correcting all pointers into it. ** In ISO C, any pointer use after the pointer has been deallocated is @@ -227,7 +242,7 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { int i; StkId newstack; int oldgcstop = G(L)->gcstopem; - lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); + lua_assert(newsize <= MAXSTACK || newsize == ERRORSTACKSIZE); relstack(L); /* change pointers to offsets */ G(L)->gcstopem = 1; /* stop emergency collection */ newstack = luaM_reallocvector(L, L->stack.p, oldsize + EXTRA_STACK, @@ -254,7 +269,7 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { */ int luaD_growstack (lua_State *L, int n, int raiseerror) { int size = stacksize(L); - if (l_unlikely(size > LUAI_MAXSTACK)) { + if (l_unlikely(size > MAXSTACK)) { /* if stack is larger than maximum, thread is already using the extra space reserved for errors, that is, thread is handling a stack error; cannot grow further than that. */ @@ -263,14 +278,14 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) { luaD_throw(L, LUA_ERRERR); /* error inside message handler */ return 0; /* if not 'raiseerror', just signal it */ } - else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */ + else if (n < MAXSTACK) { /* avoids arithmetic overflows */ int newsize = 2 * size; /* tentative new size */ int needed = cast_int(L->top.p - L->stack.p) + n; - if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ - newsize = LUAI_MAXSTACK; + if (newsize > MAXSTACK) /* cannot cross the limit */ + newsize = MAXSTACK; if (newsize < needed) /* but must respect what was asked for */ newsize = needed; - if (l_likely(newsize <= LUAI_MAXSTACK)) + if (l_likely(newsize <= MAXSTACK)) return luaD_reallocstack(L, newsize, raiseerror); } /* else stack overflow */ @@ -306,17 +321,17 @@ static int stackinuse (lua_State *L) { ** to twice the current use. (So, the final stack size is at most 2/3 the ** previous size, and half of its entries are empty.) ** As a particular case, if stack was handling a stack overflow and now -** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than +** it is not, 'max' (limited by MAXSTACK) will be smaller than ** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack ** will be reduced to a "regular" size. */ void luaD_shrinkstack (lua_State *L) { int inuse = stackinuse(L); - int max = (inuse > LUAI_MAXSTACK / 3) ? LUAI_MAXSTACK : inuse * 3; + int max = (inuse > MAXSTACK / 3) ? MAXSTACK : inuse * 3; /* if thread is currently not handling a stack overflow and its size is larger than maximum "reasonable" size, shrink it */ - if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) { - int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2; + if (inuse <= MAXSTACK && stacksize(L) > max) { + int nsize = (inuse > MAXSTACK / 2) ? MAXSTACK : inuse * 2; luaD_reallocstack(L, nsize, 0); /* ok if that fails */ } else /* don't change stack */ @@ -408,7 +423,7 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) { delta = ci->u.l.nextraargs + p->numparams + 1; } ci->func.p += delta; /* if vararg, back to virtual 'func' */ - ftransfer = cast(unsigned short, firstres - ci->func.p); + ftransfer = cast_int(firstres - ci->func.p); luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ ci->func.p -= delta; } diff --git a/lstate.h b/lstate.h index 6094016d..e5056abe 100644 --- a/lstate.h +++ b/lstate.h @@ -207,8 +207,8 @@ struct CallInfo { int nyield; /* number of values yielded */ int nres; /* number of values returned */ struct { /* info about transferred values (for call/return hooks) */ - unsigned short ftransfer; /* offset of first value transferred */ - unsigned short ntransfer; /* number of values transferred */ + int ftransfer; /* offset of first value transferred */ + int ntransfer; /* number of values transferred */ } transferinfo; } u2; short nresults; /* expected number of results from this function */ diff --git a/lua.h b/lua.h index 2f9d0abb..dcf49264 100644 --- a/lua.h +++ b/lua.h @@ -503,8 +503,8 @@ struct lua_Debug { unsigned char nparams;/* (u) number of parameters */ char isvararg; /* (u) */ char istailcall; /* (t) */ - unsigned short ftransfer; /* (r) index of first value transferred */ - unsigned short ntransfer; /* (r) number of transferred values */ + int ftransfer; /* (r) index of first value transferred */ + int ntransfer; /* (r) number of transferred values */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ struct CallInfo *i_ci; /* active function */ diff --git a/luaconf.h b/luaconf.h index 65715c8b..80349acc 100644 --- a/luaconf.h +++ b/luaconf.h @@ -750,13 +750,13 @@ @@ LUAI_MAXSTACK limits the size of the Lua stack. ** CHANGE it if you need a different limit. This limit is arbitrary; ** its only purpose is to stop Lua from consuming unlimited stack -** space (and to reserve some numbers for pseudo-indices). -** (It must fit into max(size_t)/32 and max(int)/2.) +** space and to reserve some numbers for pseudo-indices. +** (It must fit into max(int)/2.) */ -#if LUAI_IS32INT +#if 1000000 < (INT_MAX / 2) #define LUAI_MAXSTACK 1000000 #else -#define LUAI_MAXSTACK 15000 +#define LUAI_MAXSTACK (INT_MAX / 2u) #endif diff --git a/manual/manual.of b/manual/manual.of index 56619afe..1069f644 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -4262,8 +4262,9 @@ you push the main function plus any arguments onto the empty stack of the thread. then you call @Lid{lua_resume}, with @id{nargs} being the number of arguments. -This call returns when the coroutine suspends or finishes its execution. -When it returns, +The function returns when the coroutine suspends, +finishes its execution, or raises an unprotected error. +When it returns without errors, @id{*nresults} is updated and the top of the stack contains the @id{*nresults} values passed to @Lid{lua_yield} @@ -4274,9 +4275,11 @@ or returned by the body function. without errors, or an error code in case of errors @see{statuscodes}. In case of errors, -the error object is on the top of the stack. +the error object is pushed on the top of the stack. +(In that case, @id{nresults} is not updated, +as its value would have to be 1 for the sole error object.) -To resume a coroutine, +To resume a suspended coroutine, you remove the @id{*nresults} yielded values from its stack, push the values to be passed as results from @id{yield}, and then call @Lid{lua_resume}. @@ -4822,8 +4825,8 @@ typedef struct lua_Debug { unsigned char nparams; /* (u) number of parameters */ char isvararg; /* (u) */ char istailcall; /* (t) */ - unsigned short ftransfer; /* (r) index of first value transferred */ - unsigned short ntransfer; /* (r) number of transferred values */ + int ftransfer; /* (r) index of first value transferred */ + int ntransfer; /* (r) number of transferred values */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ @rep{other fields}