diff options
| author | Mike Pall <mike> | 2024-02-04 16:34:30 +0100 |
|---|---|---|
| committer | Mike Pall <mike> | 2024-02-04 16:34:30 +0100 |
| commit | defe61a56751a0db5f00ff3ab7b8f45436ba74c8 (patch) | |
| tree | c9415344aea204341e4c2620d06e624c0209ce67 | |
| parent | 9cdd5a9479d2265f42dfefc17d068174969bbcff (diff) | |
| download | luajit-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.c | 1 | ||||
| -rw-r--r-- | src/lj_err.c | 22 | ||||
| -rw-r--r-- | src/lj_err.h | 1 | ||||
| -rw-r--r-- | src/lj_state.c | 54 |
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. */ | ||
| 580 | void 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. */ |
| 571 | LJ_NORET LJ_NOINLINE static void err_msgv(lua_State *L, ErrMsg em, ...) | 587 | LJ_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; | |||
| 23 | LJ_FUNC GCstr *lj_err_str(lua_State *L, ErrMsg em); | 23 | LJ_FUNC GCstr *lj_err_str(lua_State *L, ErrMsg em); |
| 24 | LJ_FUNCA_NORET void LJ_FASTCALL lj_err_throw(lua_State *L, int errcode); | 24 | LJ_FUNCA_NORET void LJ_FASTCALL lj_err_throw(lua_State *L, int errcode); |
| 25 | LJ_FUNC_NORET void lj_err_mem(lua_State *L); | 25 | LJ_FUNC_NORET void lj_err_mem(lua_State *L); |
| 26 | LJ_FUNC_NORET void LJ_FASTCALL lj_err_stkov(lua_State *L); | ||
| 26 | LJ_FUNCA_NORET void LJ_FASTCALL lj_err_run(lua_State *L); | 27 | LJ_FUNCA_NORET void LJ_FASTCALL lj_err_run(lua_State *L); |
| 27 | LJ_FUNC_NORET void lj_err_msg(lua_State *L, ErrMsg em); | 28 | LJ_FUNC_NORET void lj_err_msg(lua_State *L, ErrMsg em); |
| 28 | LJ_FUNC_NORET void lj_err_lex(lua_State *L, GCstr *src, const char *tok, | 29 | LJ_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. */ |
| 97 | void LJ_FASTCALL lj_state_growstack(lua_State *L, MSize need) | 97 | void 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 | ||
| 122 | void LJ_FASTCALL lj_state_growstack1(lua_State *L) | 140 | void LJ_FASTCALL lj_state_growstack1(lua_State *L) |
