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) |