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.
This commit is contained in:
Roberto Ierusalimschy 2024-07-16 11:33:30 -03:00
parent 6b45ccf4ed
commit cd4de92762
5 changed files with 46 additions and 28 deletions

43
ldo.c
View File

@ -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. ** 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. ** 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 ** 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; int i;
StkId newstack; StkId newstack;
int oldgcstop = G(L)->gcstopem; int oldgcstop = G(L)->gcstopem;
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); lua_assert(newsize <= MAXSTACK || newsize == ERRORSTACKSIZE);
relstack(L); /* change pointers to offsets */ relstack(L); /* change pointers to offsets */
G(L)->gcstopem = 1; /* stop emergency collection */ G(L)->gcstopem = 1; /* stop emergency collection */
newstack = luaM_reallocvector(L, L->stack.p, oldsize + EXTRA_STACK, 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 luaD_growstack (lua_State *L, int n, int raiseerror) {
int size = stacksize(L); 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 /* if stack is larger than maximum, thread is already using the
extra space reserved for errors, that is, thread is handling extra space reserved for errors, that is, thread is handling
a stack error; cannot grow further than that. */ 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 */ luaD_throw(L, LUA_ERRERR); /* error inside message handler */
return 0; /* if not 'raiseerror', just signal it */ 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 newsize = 2 * size; /* tentative new size */
int needed = cast_int(L->top.p - L->stack.p) + n; int needed = cast_int(L->top.p - L->stack.p) + n;
if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ if (newsize > MAXSTACK) /* cannot cross the limit */
newsize = LUAI_MAXSTACK; newsize = MAXSTACK;
if (newsize < needed) /* but must respect what was asked for */ if (newsize < needed) /* but must respect what was asked for */
newsize = needed; newsize = needed;
if (l_likely(newsize <= LUAI_MAXSTACK)) if (l_likely(newsize <= MAXSTACK))
return luaD_reallocstack(L, newsize, raiseerror); return luaD_reallocstack(L, newsize, raiseerror);
} }
/* else stack overflow */ /* 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 ** 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.) ** previous size, and half of its entries are empty.)
** As a particular case, if stack was handling a stack overflow and now ** 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 ** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack
** will be reduced to a "regular" size. ** will be reduced to a "regular" size.
*/ */
void luaD_shrinkstack (lua_State *L) { void luaD_shrinkstack (lua_State *L) {
int inuse = stackinuse(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 /* if thread is currently not handling a stack overflow and its
size is larger than maximum "reasonable" size, shrink it */ size is larger than maximum "reasonable" size, shrink it */
if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) { if (inuse <= MAXSTACK && stacksize(L) > max) {
int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2; int nsize = (inuse > MAXSTACK / 2) ? MAXSTACK : inuse * 2;
luaD_reallocstack(L, nsize, 0); /* ok if that fails */ luaD_reallocstack(L, nsize, 0); /* ok if that fails */
} }
else /* don't change stack */ 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; delta = ci->u.l.nextraargs + p->numparams + 1;
} }
ci->func.p += delta; /* if vararg, back to virtual 'func' */ 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 */ luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
ci->func.p -= delta; ci->func.p -= delta;
} }

View File

@ -207,8 +207,8 @@ struct CallInfo {
int nyield; /* number of values yielded */ int nyield; /* number of values yielded */
int nres; /* number of values returned */ int nres; /* number of values returned */
struct { /* info about transferred values (for call/return hooks) */ struct { /* info about transferred values (for call/return hooks) */
unsigned short ftransfer; /* offset of first value transferred */ int ftransfer; /* offset of first value transferred */
unsigned short ntransfer; /* number of values transferred */ int ntransfer; /* number of values transferred */
} transferinfo; } transferinfo;
} u2; } u2;
short nresults; /* expected number of results from this function */ short nresults; /* expected number of results from this function */

4
lua.h
View File

@ -503,8 +503,8 @@ struct lua_Debug {
unsigned char nparams;/* (u) number of parameters */ unsigned char nparams;/* (u) number of parameters */
char isvararg; /* (u) */ char isvararg; /* (u) */
char istailcall; /* (t) */ char istailcall; /* (t) */
unsigned short ftransfer; /* (r) index of first value transferred */ int ftransfer; /* (r) index of first value transferred */
unsigned short ntransfer; /* (r) number of transferred values */ int ntransfer; /* (r) number of transferred values */
char short_src[LUA_IDSIZE]; /* (S) */ char short_src[LUA_IDSIZE]; /* (S) */
/* private part */ /* private part */
struct CallInfo *i_ci; /* active function */ struct CallInfo *i_ci; /* active function */

View File

@ -750,13 +750,13 @@
@@ LUAI_MAXSTACK limits the size of the Lua stack. @@ LUAI_MAXSTACK limits the size of the Lua stack.
** CHANGE it if you need a different limit. This limit is arbitrary; ** CHANGE it if you need a different limit. This limit is arbitrary;
** its only purpose is to stop Lua from consuming unlimited stack ** its only purpose is to stop Lua from consuming unlimited stack
** space (and to reserve some numbers for pseudo-indices). ** space and to reserve some numbers for pseudo-indices.
** (It must fit into max(size_t)/32 and max(int)/2.) ** (It must fit into max(int)/2.)
*/ */
#if LUAI_IS32INT #if 1000000 < (INT_MAX / 2)
#define LUAI_MAXSTACK 1000000 #define LUAI_MAXSTACK 1000000
#else #else
#define LUAI_MAXSTACK 15000 #define LUAI_MAXSTACK (INT_MAX / 2u)
#endif #endif

View File

@ -4262,8 +4262,9 @@ you push the main function plus any arguments
onto the empty stack of the thread. onto the empty stack of the thread.
then you call @Lid{lua_resume}, then you call @Lid{lua_resume},
with @id{nargs} being the number of arguments. with @id{nargs} being the number of arguments.
This call returns when the coroutine suspends or finishes its execution. The function returns when the coroutine suspends,
When it returns, finishes its execution, or raises an unprotected error.
When it returns without errors,
@id{*nresults} is updated and @id{*nresults} is updated and
the top of the stack contains the top of the stack contains
the @id{*nresults} values passed to @Lid{lua_yield} the @id{*nresults} values passed to @Lid{lua_yield}
@ -4274,9 +4275,11 @@ or returned by the body function.
without errors, without errors,
or an error code in case of errors @see{statuscodes}. or an error code in case of errors @see{statuscodes}.
In case of errors, 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, you remove the @id{*nresults} yielded values from its stack,
push the values to be passed as results from @id{yield}, push the values to be passed as results from @id{yield},
and then call @Lid{lua_resume}. and then call @Lid{lua_resume}.
@ -4822,8 +4825,8 @@ typedef struct lua_Debug {
unsigned char nparams; /* (u) number of parameters */ unsigned char nparams; /* (u) number of parameters */
char isvararg; /* (u) */ char isvararg; /* (u) */
char istailcall; /* (t) */ char istailcall; /* (t) */
unsigned short ftransfer; /* (r) index of first value transferred */ int ftransfer; /* (r) index of first value transferred */
unsigned short ntransfer; /* (r) number of transferred values */ int ntransfer; /* (r) number of transferred values */
char short_src[LUA_IDSIZE]; /* (S) */ char short_src[LUA_IDSIZE]; /* (S) */
/* private part */ /* private part */
@rep{other fields} @rep{other fields}