aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pall <mike>2024-02-04 16:34:30 +0100
committerMike Pall <mike>2024-02-04 16:34:30 +0100
commitdefe61a56751a0db5f00ff3ab7b8f45436ba74c8 (patch)
treec9415344aea204341e4c2620d06e624c0209ce67
parent9cdd5a9479d2265f42dfefc17d068174969bbcff (diff)
downloadluajit-defe61a56751a0db5f00ff3ab7b8f45436ba74c8.tar.gz
luajit-defe61a56751a0db5f00ff3ab7b8f45436ba74c8.tar.bz2
luajit-defe61a56751a0db5f00ff3ab7b8f45436ba74c8.zip
Rework stack overflow handling.
Reported by pwnhacker0x18. Fixed by Peter Cawley. #1152
-rw-r--r--src/lj_debug.c1
-rw-r--r--src/lj_err.c22
-rw-r--r--src/lj_err.h1
-rw-r--r--src/lj_state.c54
4 files changed, 57 insertions, 21 deletions
diff --git a/src/lj_debug.c b/src/lj_debug.c
index e6a8be54..bca1d7a5 100644
--- a/src/lj_debug.c
+++ b/src/lj_debug.c
@@ -63,6 +63,7 @@ static BCPos debug_framepc(lua_State *L, GCfunc *fn, cTValue *nextframe)
63 if (cf == NULL || (char *)cframe_pc(cf) == (char *)cframe_L(cf)) 63 if (cf == NULL || (char *)cframe_pc(cf) == (char *)cframe_L(cf))
64 return NO_BCPOS; 64 return NO_BCPOS;
65 ins = cframe_pc(cf); /* Only happens during error/hook handling. */ 65 ins = cframe_pc(cf); /* Only happens during error/hook handling. */
66 if (!ins) return NO_BCPOS;
66 } else { 67 } else {
67 if (frame_islua(nextframe)) { 68 if (frame_islua(nextframe)) {
68 ins = frame_pc(nextframe); 69 ins = frame_pc(nextframe);
diff --git a/src/lj_err.c b/src/lj_err.c
index 4a2d6bbd..7afe1e29 100644
--- a/src/lj_err.c
+++ b/src/lj_err.c
@@ -488,7 +488,14 @@ LJ_NOINLINE void lj_err_mem(lua_State *L)
488{ 488{
489 if (L->status == LUA_ERRERR+1) /* Don't touch the stack during lua_open. */ 489 if (L->status == LUA_ERRERR+1) /* Don't touch the stack during lua_open. */
490 lj_vm_unwind_c(L->cframe, LUA_ERRMEM); 490 lj_vm_unwind_c(L->cframe, LUA_ERRMEM);
491 if (curr_funcisL(L)) L->top = curr_topL(L); 491 if (curr_funcisL(L)) {
492 L->top = curr_topL(L);
493 if (LJ_UNLIKELY(L->top > tvref(L->maxstack))) {
494 /* The current Lua frame violates the stack. Replace it with a dummy. */
495 L->top = L->base;
496 setframe_gc(L->base - 1, obj2gco(L));
497 }
498 }
492 setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRMEM)); 499 setstrV(L, L->top++, lj_err_str(L, LJ_ERR_ERRMEM));
493 lj_err_throw(L, LUA_ERRMEM); 500 lj_err_throw(L, LUA_ERRMEM);
494} 501}
@@ -551,9 +558,11 @@ LJ_NOINLINE void LJ_FASTCALL lj_err_run(lua_State *L)
551{ 558{
552 ptrdiff_t ef = finderrfunc(L); 559 ptrdiff_t ef = finderrfunc(L);
553 if (ef) { 560 if (ef) {
554 TValue *errfunc = restorestack(L, ef); 561 TValue *errfunc, *top;
555 TValue *top = L->top; 562 lj_state_checkstack(L, LUA_MINSTACK * 2); /* Might raise new error. */
556 lj_trace_abort(G(L)); 563 lj_trace_abort(G(L));
564 errfunc = restorestack(L, ef);
565 top = L->top;
557 if (!tvisfunc(errfunc) || L->status == LUA_ERRERR) { 566 if (!tvisfunc(errfunc) || L->status == LUA_ERRERR) {
558 setstrV(L, top-1, lj_err_str(L, LJ_ERR_ERRERR)); 567 setstrV(L, top-1, lj_err_str(L, LJ_ERR_ERRERR));
559 lj_err_throw(L, LUA_ERRERR); 568 lj_err_throw(L, LUA_ERRERR);
@@ -567,6 +576,13 @@ LJ_NOINLINE void LJ_FASTCALL lj_err_run(lua_State *L)
567 lj_err_throw(L, LUA_ERRRUN); 576 lj_err_throw(L, LUA_ERRRUN);
568} 577}
569 578
579/* Stack overflow error. */
580void LJ_FASTCALL lj_err_stkov(lua_State *L)
581{
582 lj_debug_addloc(L, err2msg(LJ_ERR_STKOV), L->base-1, NULL);
583 lj_err_run(L);
584}
585
570/* Formatted runtime error message. */ 586/* Formatted runtime error message. */
571LJ_NORET LJ_NOINLINE static void err_msgv(lua_State *L, ErrMsg em, ...) 587LJ_NORET LJ_NOINLINE static void err_msgv(lua_State *L, ErrMsg em, ...)
572{ 588{
diff --git a/src/lj_err.h b/src/lj_err.h
index 321719a9..15040922 100644
--- a/src/lj_err.h
+++ b/src/lj_err.h
@@ -23,6 +23,7 @@ LJ_DATA const char *lj_err_allmsg;
23LJ_FUNC GCstr *lj_err_str(lua_State *L, ErrMsg em); 23LJ_FUNC GCstr *lj_err_str(lua_State *L, ErrMsg em);
24LJ_FUNCA_NORET void LJ_FASTCALL lj_err_throw(lua_State *L, int errcode); 24LJ_FUNCA_NORET void LJ_FASTCALL lj_err_throw(lua_State *L, int errcode);
25LJ_FUNC_NORET void lj_err_mem(lua_State *L); 25LJ_FUNC_NORET void lj_err_mem(lua_State *L);
26LJ_FUNC_NORET void LJ_FASTCALL lj_err_stkov(lua_State *L);
26LJ_FUNCA_NORET void LJ_FASTCALL lj_err_run(lua_State *L); 27LJ_FUNCA_NORET void LJ_FASTCALL lj_err_run(lua_State *L);
27LJ_FUNC_NORET void lj_err_msg(lua_State *L, ErrMsg em); 28LJ_FUNC_NORET void lj_err_msg(lua_State *L, ErrMsg em);
28LJ_FUNC_NORET void lj_err_lex(lua_State *L, GCstr *src, const char *tok, 29LJ_FUNC_NORET void lj_err_lex(lua_State *L, GCstr *src, const char *tok,
diff --git a/src/lj_state.c b/src/lj_state.c
index c2f0b115..adedb66c 100644
--- a/src/lj_state.c
+++ b/src/lj_state.c
@@ -96,27 +96,45 @@ void lj_state_shrinkstack(lua_State *L, MSize used)
96/* Try to grow stack. */ 96/* Try to grow stack. */
97void LJ_FASTCALL lj_state_growstack(lua_State *L, MSize need) 97void LJ_FASTCALL lj_state_growstack(lua_State *L, MSize need)
98{ 98{
99 MSize n; 99 MSize n = L->stacksize + need;
100 if (L->stacksize >= LJ_STACK_MAXEX) { 100 if (LJ_LIKELY(n < LJ_STACK_MAX)) { /* The stack can grow as requested. */
101 /* 4. Throw 'error in error handling' when we are _over_ the limit. */ 101 if (n < 2 * L->stacksize) { /* Try to double the size. */
102 if (L->stacksize > LJ_STACK_MAXEX) 102 n = 2 * L->stacksize;
103 if (n > LJ_STACK_MAX)
104 n = LJ_STACK_MAX;
105 }
106 resizestack(L, n);
107 } else { /* Request would overflow. Raise a stack overflow error. */
108 if (curr_funcisL(L)) {
109 L->top = curr_topL(L);
110 if (L->top > tvref(L->maxstack)) {
111 /* The current Lua frame violates the stack, so replace it with a
112 ** dummy. This can happen when BC_IFUNCF is trying to grow the stack.
113 */
114 L->top = L->base;
115 setframe_gc(L->base - 1, obj2gco(L));
116 }
117 }
118 if (L->stacksize <= LJ_STACK_MAXEX) {
119 /* An error handler might want to inspect the stack overflow error, but
120 ** will need some stack space to run in. We give it a stack size beyond
121 ** the normal limit in order to do so, then rely on lj_state_relimitstack
122 ** calls during unwinding to bring us back to a convential stack size.
123 ** The + 1 is space for the error message, and 2 * LUA_MINSTACK is for
124 ** the lj_state_checkstack() call in lj_err_run().
125 */
126 resizestack(L, LJ_STACK_MAX + 1 + 2 * LUA_MINSTACK);
127 lj_err_stkov(L); /* May invoke an error handler. */
128 } else {
129 /* If we're here, then the stack overflow error handler is requesting
130 ** to grow the stack even further. We have no choice but to abort the
131 ** error handler.
132 */
133 GCstr *em = lj_err_str(L, LJ_ERR_STKOV); /* Might OOM. */
134 setstrV(L, L->top++, em); /* There is always space to push an error. */
103 lj_err_throw(L, LUA_ERRERR); /* Does not invoke an error handler. */ 135 lj_err_throw(L, LUA_ERRERR); /* Does not invoke an error handler. */
104 /* 1. We are _at_ the limit after the last growth. */
105 if (L->status < LUA_ERRRUN) { /* 2. Throw 'stack overflow'. */
106 L->status = LUA_ERRRUN; /* Prevent ending here again for pushed msg. */
107 lj_err_msg(L, LJ_ERR_STKOV); /* May invoke an error handler. */
108 } 136 }
109 /* 3. Add space (over the limit) for pushed message and error handler. */
110 }
111 n = L->stacksize + need;
112 if (n > LJ_STACK_MAX) {
113 n += 2*LUA_MINSTACK;
114 } else if (n < 2*L->stacksize) {
115 n = 2*L->stacksize;
116 if (n >= LJ_STACK_MAX)
117 n = LJ_STACK_MAX;
118 } 137 }
119 resizestack(L, n);
120} 138}
121 139
122void LJ_FASTCALL lj_state_growstack1(lua_State *L) 140void LJ_FASTCALL lj_state_growstack1(lua_State *L)