mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-26 11:19:32 +03:00
31b4e008f1
Apply the fix from upstream. http://www.lua.org/bugs.html#5.2.2-1 https://www.opencve.io/cve/CVE-2014-5461 It should be noted that exploiting this requires the `SYS_CONFIG` privilege, and anyone with that privilege likely has other opportunities to do exploits, so it is unlikely that bad actors could exploit this unless system administrators are executing untrusted ZFS Channel Programs. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Richard Yao <richard.yao@alumni.stonybrook.edu> Closes #13949
759 lines
22 KiB
C
759 lines
22 KiB
C
/*
|
|
** $Id: ldo.c,v 2.108.1.3 2013/11/08 18:22:50 roberto Exp $
|
|
** Stack and Call structure of Lua
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
|
|
|
|
#define ldo_c
|
|
#define LUA_CORE
|
|
|
|
#include <sys/lua/lua.h>
|
|
|
|
#include "lapi.h"
|
|
#include "ldebug.h"
|
|
#include "ldo.h"
|
|
#include "lfunc.h"
|
|
#include "lgc.h"
|
|
#include "lmem.h"
|
|
#include "lobject.h"
|
|
#include "lopcodes.h"
|
|
#include "lparser.h"
|
|
#include "lstate.h"
|
|
#include "lstring.h"
|
|
#include "ltable.h"
|
|
#include "ltm.h"
|
|
#include "lvm.h"
|
|
#include "lzio.h"
|
|
|
|
|
|
|
|
/* Return the number of bytes available on the stack. */
|
|
#if defined (_KERNEL) && defined(__linux__)
|
|
#include <asm/current.h>
|
|
static intptr_t stack_remaining(void) {
|
|
intptr_t local;
|
|
local = (intptr_t)&local - (intptr_t)current->stack;
|
|
return local;
|
|
}
|
|
#elif defined (_KERNEL) && defined(__FreeBSD__)
|
|
#include <sys/pcpu.h>
|
|
static intptr_t stack_remaining(void) {
|
|
intptr_t local;
|
|
local = (intptr_t)&local - (intptr_t)curthread->td_kstack;
|
|
return local;
|
|
}
|
|
#else
|
|
static intptr_t stack_remaining(void) {
|
|
return INTPTR_MAX;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
** {======================================================
|
|
** Error-recovery functions
|
|
** =======================================================
|
|
*/
|
|
|
|
/*
|
|
** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By
|
|
** default, Lua handles errors with exceptions when compiling as
|
|
** C++ code, with _longjmp/_setjmp when asked to use them, and with
|
|
** longjmp/setjmp otherwise.
|
|
*/
|
|
#if !defined(LUAI_THROW)
|
|
|
|
#ifdef _KERNEL
|
|
|
|
#ifdef __linux__
|
|
#if defined(__i386__)
|
|
#define JMP_BUF_CNT 6
|
|
#elif defined(__x86_64__)
|
|
#define JMP_BUF_CNT 8
|
|
#elif defined(__sparc__) && defined(__arch64__)
|
|
#define JMP_BUF_CNT 6
|
|
#elif defined(__powerpc__)
|
|
#define JMP_BUF_CNT 26
|
|
#elif defined(__aarch64__)
|
|
#define JMP_BUF_CNT 64
|
|
#elif defined(__arm__)
|
|
#define JMP_BUF_CNT 65
|
|
#elif defined(__mips__)
|
|
#define JMP_BUF_CNT 12
|
|
#elif defined(__s390x__)
|
|
#define JMP_BUF_CNT 18
|
|
#elif defined(__riscv)
|
|
#define JMP_BUF_CNT 64
|
|
#else
|
|
#define JMP_BUF_CNT 1
|
|
#endif
|
|
|
|
typedef struct _label_t { long long unsigned val[JMP_BUF_CNT]; } label_t;
|
|
|
|
int setjmp(label_t *) __attribute__ ((__nothrow__));
|
|
extern __attribute__((noreturn)) void longjmp(label_t *);
|
|
|
|
#define LUAI_THROW(L,c) longjmp(&(c)->b)
|
|
#define LUAI_TRY(L,c,a) if (setjmp(&(c)->b) == 0) { a }
|
|
#define luai_jmpbuf label_t
|
|
|
|
/* unsupported arches will build but not be able to run lua programs */
|
|
#if JMP_BUF_CNT == 1
|
|
int setjmp (label_t *buf) {
|
|
return 1;
|
|
}
|
|
|
|
void longjmp (label_t * buf) {
|
|
for (;;);
|
|
}
|
|
#endif
|
|
#else
|
|
#define LUAI_THROW(L,c) longjmp((c)->b, 1)
|
|
#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
|
|
#define luai_jmpbuf jmp_buf
|
|
#endif
|
|
|
|
#else /* _KERNEL */
|
|
|
|
#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP)
|
|
/* C++ exceptions */
|
|
#define LUAI_THROW(L,c) throw(c)
|
|
#define LUAI_TRY(L,c,a) \
|
|
try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; }
|
|
#define luai_jmpbuf int /* dummy variable */
|
|
|
|
#elif defined(LUA_USE_ULONGJMP)
|
|
/* in Unix, try _longjmp/_setjmp (more efficient) */
|
|
#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
|
|
#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
|
|
#define luai_jmpbuf jmp_buf
|
|
|
|
#else
|
|
/* default handling with long jumps */
|
|
#define LUAI_THROW(L,c) longjmp((c)->b, 1)
|
|
#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
|
|
#define luai_jmpbuf jmp_buf
|
|
|
|
#endif
|
|
|
|
#endif /* _KERNEL */
|
|
|
|
#endif /* LUAI_THROW */
|
|
|
|
|
|
/* chain list of long jump buffers */
|
|
struct lua_longjmp {
|
|
struct lua_longjmp *previous;
|
|
luai_jmpbuf b;
|
|
volatile int status; /* error code */
|
|
};
|
|
|
|
|
|
static void seterrorobj (lua_State *L, int errcode, StkId oldtop) {
|
|
switch (errcode) {
|
|
case LUA_ERRMEM: { /* memory error? */
|
|
setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
|
|
break;
|
|
}
|
|
case LUA_ERRERR: {
|
|
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
|
|
break;
|
|
}
|
|
default: {
|
|
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
|
|
break;
|
|
}
|
|
}
|
|
L->top = oldtop + 1;
|
|
}
|
|
|
|
/*
|
|
* Silence infinite recursion warning which was added to -Wall in gcc 12.1
|
|
*/
|
|
#if defined(HAVE_INFINITE_RECURSION)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Winfinite-recursion"
|
|
#endif
|
|
|
|
l_noret luaD_throw (lua_State *L, int errcode) {
|
|
if (L->errorJmp) { /* thread has an error handler? */
|
|
L->errorJmp->status = errcode; /* set status */
|
|
LUAI_THROW(L, L->errorJmp); /* jump to it */
|
|
}
|
|
else { /* thread has no error handler */
|
|
L->status = cast_byte(errcode); /* mark it as dead */
|
|
if (G(L)->mainthread->errorJmp) { /* main thread has a handler? */
|
|
setobjs2s(L, G(L)->mainthread->top++, L->top - 1); /* copy error obj. */
|
|
luaD_throw(G(L)->mainthread, errcode); /* re-throw in main thread */
|
|
}
|
|
else { /* no handler at all; abort */
|
|
if (G(L)->panic) { /* panic function? */
|
|
lua_unlock(L);
|
|
G(L)->panic(L); /* call it (last chance to jump out) */
|
|
}
|
|
panic("no error handler");
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(HAVE_INFINITE_RECURSION)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
|
|
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
|
|
unsigned short oldnCcalls = L->nCcalls;
|
|
struct lua_longjmp lj;
|
|
lj.status = LUA_OK;
|
|
lj.previous = L->errorJmp; /* chain new error handler */
|
|
L->errorJmp = &lj;
|
|
LUAI_TRY(L, &lj,
|
|
(*f)(L, ud);
|
|
);
|
|
L->errorJmp = lj.previous; /* restore old error handler */
|
|
L->nCcalls = oldnCcalls;
|
|
return lj.status;
|
|
}
|
|
|
|
/* }====================================================== */
|
|
|
|
|
|
static void correctstack (lua_State *L, TValue *oldstack) {
|
|
CallInfo *ci;
|
|
GCObject *up;
|
|
L->top = (L->top - oldstack) + L->stack;
|
|
for (up = L->openupval; up != NULL; up = up->gch.next)
|
|
gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;
|
|
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
|
ci->top = (ci->top - oldstack) + L->stack;
|
|
ci->func = (ci->func - oldstack) + L->stack;
|
|
if (isLua(ci))
|
|
ci->u.l.base = (ci->u.l.base - oldstack) + L->stack;
|
|
}
|
|
}
|
|
|
|
|
|
/* some space for error handling */
|
|
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
|
|
|
|
|
|
void luaD_reallocstack (lua_State *L, int newsize) {
|
|
TValue *oldstack = L->stack;
|
|
int lim = L->stacksize;
|
|
lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
|
|
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK);
|
|
luaM_reallocvector(L, L->stack, L->stacksize, newsize, TValue);
|
|
for (; lim < newsize; lim++)
|
|
setnilvalue(L->stack + lim); /* erase new segment */
|
|
L->stacksize = newsize;
|
|
L->stack_last = L->stack + newsize - EXTRA_STACK;
|
|
correctstack(L, oldstack);
|
|
}
|
|
|
|
|
|
void luaD_growstack (lua_State *L, int n) {
|
|
int size = L->stacksize;
|
|
if (size > LUAI_MAXSTACK) /* error after extra size? */
|
|
luaD_throw(L, LUA_ERRERR);
|
|
else {
|
|
int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK;
|
|
int newsize = 2 * size;
|
|
if (newsize > LUAI_MAXSTACK) newsize = LUAI_MAXSTACK;
|
|
if (newsize < needed) newsize = needed;
|
|
if (newsize > LUAI_MAXSTACK) { /* stack overflow? */
|
|
luaD_reallocstack(L, ERRORSTACKSIZE);
|
|
luaG_runerror(L, "stack overflow");
|
|
}
|
|
else
|
|
luaD_reallocstack(L, newsize);
|
|
}
|
|
}
|
|
|
|
|
|
static int stackinuse (lua_State *L) {
|
|
CallInfo *ci;
|
|
StkId lim = L->top;
|
|
for (ci = L->ci; ci != NULL; ci = ci->previous) {
|
|
lua_assert(ci->top <= L->stack_last);
|
|
if (lim < ci->top) lim = ci->top;
|
|
}
|
|
return cast_int(lim - L->stack) + 1; /* part of stack in use */
|
|
}
|
|
|
|
|
|
void luaD_shrinkstack (lua_State *L) {
|
|
int inuse = stackinuse(L);
|
|
int goodsize = inuse + (inuse / 8) + 2*EXTRA_STACK;
|
|
if (goodsize > LUAI_MAXSTACK) goodsize = LUAI_MAXSTACK;
|
|
if (inuse > LUAI_MAXSTACK || /* handling stack overflow? */
|
|
goodsize >= L->stacksize) /* would grow instead of shrink? */
|
|
condmovestack(L); /* don't change stack (change only for debugging) */
|
|
else
|
|
luaD_reallocstack(L, goodsize); /* shrink it */
|
|
}
|
|
|
|
|
|
void luaD_hook (lua_State *L, int event, int line) {
|
|
lua_Hook hook = L->hook;
|
|
if (hook && L->allowhook) {
|
|
CallInfo *ci = L->ci;
|
|
ptrdiff_t top = savestack(L, L->top);
|
|
ptrdiff_t ci_top = savestack(L, ci->top);
|
|
lua_Debug ar;
|
|
ar.event = event;
|
|
ar.currentline = line;
|
|
ar.i_ci = ci;
|
|
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
|
|
ci->top = L->top + LUA_MINSTACK;
|
|
lua_assert(ci->top <= L->stack_last);
|
|
L->allowhook = 0; /* cannot call hooks inside a hook */
|
|
ci->callstatus |= CIST_HOOKED;
|
|
lua_unlock(L);
|
|
(*hook)(L, &ar);
|
|
lua_lock(L);
|
|
lua_assert(!L->allowhook);
|
|
L->allowhook = 1;
|
|
ci->top = restorestack(L, ci_top);
|
|
L->top = restorestack(L, top);
|
|
ci->callstatus &= ~CIST_HOOKED;
|
|
}
|
|
}
|
|
|
|
|
|
static void callhook (lua_State *L, CallInfo *ci) {
|
|
int hook = LUA_HOOKCALL;
|
|
ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
|
|
if (isLua(ci->previous) &&
|
|
GET_OPCODE(*(ci->previous->u.l.savedpc - 1)) == OP_TAILCALL) {
|
|
ci->callstatus |= CIST_TAIL;
|
|
hook = LUA_HOOKTAILCALL;
|
|
}
|
|
luaD_hook(L, hook, -1);
|
|
ci->u.l.savedpc--; /* correct 'pc' */
|
|
}
|
|
|
|
|
|
static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
|
|
int i;
|
|
int nfixargs = p->numparams;
|
|
StkId base, fixed;
|
|
lua_assert(actual >= nfixargs);
|
|
/* move fixed parameters to final position */
|
|
luaD_checkstack(L, p->maxstacksize); /* check again for new 'base' */
|
|
fixed = L->top - actual; /* first fixed argument */
|
|
base = L->top; /* final position of first argument */
|
|
for (i=0; i<nfixargs; i++) {
|
|
setobjs2s(L, L->top++, fixed + i);
|
|
setnilvalue(fixed + i);
|
|
}
|
|
return base;
|
|
}
|
|
|
|
|
|
static StkId tryfuncTM (lua_State *L, StkId func) {
|
|
const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);
|
|
StkId p;
|
|
ptrdiff_t funcr = savestack(L, func);
|
|
if (!ttisfunction(tm))
|
|
luaG_typeerror(L, func, "call");
|
|
/* Open a hole inside the stack at `func' */
|
|
for (p = L->top; p > func; p--) setobjs2s(L, p, p-1);
|
|
incr_top(L);
|
|
func = restorestack(L, funcr); /* previous call may change stack */
|
|
setobj2s(L, func, tm); /* tag method is the new function to be called */
|
|
return func;
|
|
}
|
|
|
|
|
|
|
|
#define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L)))
|
|
|
|
|
|
/*
|
|
** returns true if function has been executed (C function)
|
|
*/
|
|
int luaD_precall (lua_State *L, StkId func, int nresults) {
|
|
lua_CFunction f;
|
|
CallInfo *ci;
|
|
int n; /* number of arguments (Lua) or returns (C) */
|
|
ptrdiff_t funcr = savestack(L, func);
|
|
switch (ttype(func)) {
|
|
case LUA_TLCF: /* light C function */
|
|
f = fvalue(func);
|
|
goto Cfunc;
|
|
case LUA_TCCL: { /* C closure */
|
|
f = clCvalue(func)->f;
|
|
Cfunc:
|
|
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
|
|
ci = next_ci(L); /* now 'enter' new function */
|
|
ci->nresults = nresults;
|
|
ci->func = restorestack(L, funcr);
|
|
ci->top = L->top + LUA_MINSTACK;
|
|
lua_assert(ci->top <= L->stack_last);
|
|
ci->callstatus = 0;
|
|
luaC_checkGC(L); /* stack grow uses memory */
|
|
if (L->hookmask & LUA_MASKCALL)
|
|
luaD_hook(L, LUA_HOOKCALL, -1);
|
|
lua_unlock(L);
|
|
n = (*f)(L); /* do the actual call */
|
|
lua_lock(L);
|
|
api_checknelems(L, n);
|
|
luaD_poscall(L, L->top - n);
|
|
return 1;
|
|
}
|
|
case LUA_TLCL: { /* Lua function: prepare its call */
|
|
StkId base;
|
|
Proto *p = clLvalue(func)->p;
|
|
n = cast_int(L->top - func) - 1; /* number of real arguments */
|
|
luaD_checkstack(L, p->maxstacksize + p->numparams);
|
|
for (; n < p->numparams; n++)
|
|
setnilvalue(L->top++); /* complete missing arguments */
|
|
if (!p->is_vararg) {
|
|
func = restorestack(L, funcr);
|
|
base = func + 1;
|
|
}
|
|
else {
|
|
base = adjust_varargs(L, p, n);
|
|
func = restorestack(L, funcr); /* previous call can change stack */
|
|
}
|
|
ci = next_ci(L); /* now 'enter' new function */
|
|
ci->nresults = nresults;
|
|
ci->func = func;
|
|
ci->u.l.base = base;
|
|
ci->top = base + p->maxstacksize;
|
|
lua_assert(ci->top <= L->stack_last);
|
|
ci->u.l.savedpc = p->code; /* starting point */
|
|
ci->callstatus = CIST_LUA;
|
|
L->top = ci->top;
|
|
luaC_checkGC(L); /* stack grow uses memory */
|
|
if (L->hookmask & LUA_MASKCALL)
|
|
callhook(L, ci);
|
|
return 0;
|
|
}
|
|
default: { /* not a function */
|
|
func = tryfuncTM(L, func); /* retry with 'function' tag method */
|
|
return luaD_precall(L, func, nresults); /* now it must be a function */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int luaD_poscall (lua_State *L, StkId firstResult) {
|
|
StkId res;
|
|
int wanted, i;
|
|
CallInfo *ci = L->ci;
|
|
if (L->hookmask & (LUA_MASKRET | LUA_MASKLINE)) {
|
|
if (L->hookmask & LUA_MASKRET) {
|
|
ptrdiff_t fr = savestack(L, firstResult); /* hook may change stack */
|
|
luaD_hook(L, LUA_HOOKRET, -1);
|
|
firstResult = restorestack(L, fr);
|
|
}
|
|
L->oldpc = ci->previous->u.l.savedpc; /* 'oldpc' for caller function */
|
|
}
|
|
res = ci->func; /* res == final position of 1st result */
|
|
wanted = ci->nresults;
|
|
L->ci = ci = ci->previous; /* back to caller */
|
|
/* move results to correct place */
|
|
for (i = wanted; i != 0 && firstResult < L->top; i--)
|
|
setobjs2s(L, res++, firstResult++);
|
|
while (i-- > 0)
|
|
setnilvalue(res++);
|
|
L->top = res;
|
|
return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */
|
|
}
|
|
|
|
|
|
/*
|
|
** Call a function (C or Lua). The function to be called is at *func.
|
|
** The arguments are on the stack, right after the function.
|
|
** When returns, all the results are on the stack, starting at the original
|
|
** function position.
|
|
*/
|
|
void luaD_call (lua_State *L, StkId func, int nResults, int allowyield) {
|
|
if (++L->nCcalls >= LUAI_MAXCCALLS) {
|
|
if (L->nCcalls == LUAI_MAXCCALLS)
|
|
luaG_runerror(L, "C stack overflow");
|
|
else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
|
|
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
|
|
}
|
|
intptr_t remaining = stack_remaining();
|
|
if (L->runerror == 0 && remaining < LUAI_MINCSTACK)
|
|
luaG_runerror(L, "C stack overflow");
|
|
if (L->runerror != 0 && remaining < LUAI_MINCSTACK / 2)
|
|
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
|
|
if (!allowyield) L->nny++;
|
|
if (!luaD_precall(L, func, nResults)) /* is a Lua function? */
|
|
luaV_execute(L); /* call it */
|
|
if (!allowyield) L->nny--;
|
|
L->nCcalls--;
|
|
}
|
|
|
|
|
|
static void finishCcall (lua_State *L) {
|
|
CallInfo *ci = L->ci;
|
|
int n;
|
|
lua_assert(ci->u.c.k != NULL); /* must have a continuation */
|
|
lua_assert(L->nny == 0);
|
|
if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */
|
|
ci->callstatus &= ~CIST_YPCALL; /* finish 'lua_pcall' */
|
|
L->errfunc = ci->u.c.old_errfunc;
|
|
}
|
|
/* finish 'lua_callk'/'lua_pcall' */
|
|
adjustresults(L, ci->nresults);
|
|
/* call continuation function */
|
|
if (!(ci->callstatus & CIST_STAT)) /* no call status? */
|
|
ci->u.c.status = LUA_YIELD; /* 'default' status */
|
|
lua_assert(ci->u.c.status != LUA_OK);
|
|
ci->callstatus = (ci->callstatus & ~(CIST_YPCALL | CIST_STAT)) | CIST_YIELDED;
|
|
lua_unlock(L);
|
|
n = (*ci->u.c.k)(L);
|
|
lua_lock(L);
|
|
api_checknelems(L, n);
|
|
/* finish 'luaD_precall' */
|
|
luaD_poscall(L, L->top - n);
|
|
}
|
|
|
|
|
|
static void unroll (lua_State *L, void *ud) {
|
|
UNUSED(ud);
|
|
for (;;) {
|
|
if (L->ci == &L->base_ci) /* stack is empty? */
|
|
return; /* coroutine finished normally */
|
|
if (!isLua(L->ci)) /* C function? */
|
|
finishCcall(L);
|
|
else { /* Lua function */
|
|
luaV_finishOp(L); /* finish interrupted instruction */
|
|
luaV_execute(L); /* execute down to higher C 'boundary' */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** check whether thread has a suspended protected call
|
|
*/
|
|
static CallInfo *findpcall (lua_State *L) {
|
|
CallInfo *ci;
|
|
for (ci = L->ci; ci != NULL; ci = ci->previous) { /* search for a pcall */
|
|
if (ci->callstatus & CIST_YPCALL)
|
|
return ci;
|
|
}
|
|
return NULL; /* no pending pcall */
|
|
}
|
|
|
|
|
|
static int recover (lua_State *L, int status) {
|
|
StkId oldtop;
|
|
CallInfo *ci = findpcall(L);
|
|
if (ci == NULL) return 0; /* no recovery point */
|
|
/* "finish" luaD_pcall */
|
|
oldtop = restorestack(L, ci->extra);
|
|
luaF_close(L, oldtop);
|
|
seterrorobj(L, status, oldtop);
|
|
L->ci = ci;
|
|
L->allowhook = ci->u.c.old_allowhook;
|
|
L->nny = 0; /* should be zero to be yieldable */
|
|
luaD_shrinkstack(L);
|
|
L->errfunc = ci->u.c.old_errfunc;
|
|
ci->callstatus |= CIST_STAT; /* call has error status */
|
|
ci->u.c.status = status; /* (here it is) */
|
|
return 1; /* continue running the coroutine */
|
|
}
|
|
|
|
|
|
/*
|
|
** signal an error in the call to 'resume', not in the execution of the
|
|
** coroutine itself. (Such errors should not be handled by any coroutine
|
|
** error handler and should not kill the coroutine.)
|
|
*/
|
|
static l_noret resume_error (lua_State *L, const char *msg, StkId firstArg) {
|
|
L->top = firstArg; /* remove args from the stack */
|
|
setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */
|
|
api_incr_top(L);
|
|
luaD_throw(L, -1); /* jump back to 'lua_resume' */
|
|
}
|
|
|
|
|
|
/*
|
|
** do the work for 'lua_resume' in protected mode
|
|
*/
|
|
static void resume_cb (lua_State *L, void *ud) {
|
|
int nCcalls = L->nCcalls;
|
|
StkId firstArg = cast(StkId, ud);
|
|
CallInfo *ci = L->ci;
|
|
if (nCcalls >= LUAI_MAXCCALLS)
|
|
resume_error(L, "C stack overflow", firstArg);
|
|
if (L->status == LUA_OK) { /* may be starting a coroutine */
|
|
if (ci != &L->base_ci) /* not in base level? */
|
|
resume_error(L, "cannot resume non-suspended coroutine", firstArg);
|
|
/* coroutine is in base level; start running it */
|
|
if (!luaD_precall(L, firstArg - 1, LUA_MULTRET)) /* Lua function? */
|
|
luaV_execute(L); /* call it */
|
|
}
|
|
else if (L->status != LUA_YIELD)
|
|
resume_error(L, "cannot resume dead coroutine", firstArg);
|
|
else { /* resuming from previous yield */
|
|
L->status = LUA_OK;
|
|
ci->func = restorestack(L, ci->extra);
|
|
if (isLua(ci)) /* yielded inside a hook? */
|
|
luaV_execute(L); /* just continue running Lua code */
|
|
else { /* 'common' yield */
|
|
if (ci->u.c.k != NULL) { /* does it have a continuation? */
|
|
int n;
|
|
ci->u.c.status = LUA_YIELD; /* 'default' status */
|
|
ci->callstatus |= CIST_YIELDED;
|
|
lua_unlock(L);
|
|
n = (*ci->u.c.k)(L); /* call continuation */
|
|
lua_lock(L);
|
|
api_checknelems(L, n);
|
|
firstArg = L->top - n; /* yield results come from continuation */
|
|
}
|
|
luaD_poscall(L, firstArg); /* finish 'luaD_precall' */
|
|
}
|
|
unroll(L, NULL);
|
|
}
|
|
lua_assert(nCcalls == L->nCcalls);
|
|
}
|
|
|
|
|
|
LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs) {
|
|
int status;
|
|
int oldnny = L->nny; /* save 'nny' */
|
|
lua_lock(L);
|
|
luai_userstateresume(L, nargs);
|
|
L->nCcalls = (from) ? from->nCcalls + 1 : 1;
|
|
L->nny = 0; /* allow yields */
|
|
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
|
|
status = luaD_rawrunprotected(L, resume_cb, L->top - nargs);
|
|
if (status == -1) /* error calling 'lua_resume'? */
|
|
status = LUA_ERRRUN;
|
|
else { /* yield or regular error */
|
|
while (status != LUA_OK && status != LUA_YIELD) { /* error? */
|
|
if (recover(L, status)) /* recover point? */
|
|
status = luaD_rawrunprotected(L, unroll, NULL); /* run continuation */
|
|
else { /* unrecoverable error */
|
|
L->status = cast_byte(status); /* mark thread as `dead' */
|
|
seterrorobj(L, status, L->top);
|
|
L->ci->top = L->top;
|
|
break;
|
|
}
|
|
}
|
|
lua_assert(status == L->status);
|
|
}
|
|
L->nny = oldnny; /* restore 'nny' */
|
|
L->nCcalls--;
|
|
lua_assert(L->nCcalls == ((from) ? from->nCcalls : 0));
|
|
lua_unlock(L);
|
|
return status;
|
|
}
|
|
|
|
|
|
LUA_API int lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) {
|
|
CallInfo *ci = L->ci;
|
|
luai_userstateyield(L, nresults);
|
|
lua_lock(L);
|
|
api_checknelems(L, nresults);
|
|
if (L->nny > 0) {
|
|
if (L != G(L)->mainthread)
|
|
luaG_runerror(L, "attempt to yield across a C-call boundary");
|
|
else
|
|
luaG_runerror(L, "attempt to yield from outside a coroutine");
|
|
}
|
|
L->status = LUA_YIELD;
|
|
ci->extra = savestack(L, ci->func); /* save current 'func' */
|
|
if (isLua(ci)) { /* inside a hook? */
|
|
api_check(L, k == NULL, "hooks cannot continue after yielding");
|
|
}
|
|
else {
|
|
if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
|
|
ci->u.c.ctx = ctx; /* save context */
|
|
ci->func = L->top - nresults - 1; /* protect stack below results */
|
|
luaD_throw(L, LUA_YIELD);
|
|
}
|
|
lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */
|
|
lua_unlock(L);
|
|
return 0; /* return to 'luaD_hook' */
|
|
}
|
|
|
|
|
|
int luaD_pcall (lua_State *L, Pfunc func, void *u,
|
|
ptrdiff_t old_top, ptrdiff_t ef) {
|
|
int status;
|
|
CallInfo *old_ci = L->ci;
|
|
lu_byte old_allowhooks = L->allowhook;
|
|
unsigned short old_nny = L->nny;
|
|
ptrdiff_t old_errfunc = L->errfunc;
|
|
L->errfunc = ef;
|
|
status = luaD_rawrunprotected(L, func, u);
|
|
if (status != LUA_OK) { /* an error occurred? */
|
|
StkId oldtop = restorestack(L, old_top);
|
|
luaF_close(L, oldtop); /* close possible pending closures */
|
|
seterrorobj(L, status, oldtop);
|
|
L->ci = old_ci;
|
|
L->allowhook = old_allowhooks;
|
|
L->nny = old_nny;
|
|
luaD_shrinkstack(L);
|
|
}
|
|
L->errfunc = old_errfunc;
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** Execute a protected parser.
|
|
*/
|
|
struct SParser { /* data to `f_parser' */
|
|
ZIO *z;
|
|
Mbuffer buff; /* dynamic structure used by the scanner */
|
|
Dyndata dyd; /* dynamic structures used by the parser */
|
|
const char *mode;
|
|
const char *name;
|
|
};
|
|
|
|
|
|
static void checkmode (lua_State *L, const char *mode, const char *x) {
|
|
if (mode && strchr(mode, x[0]) == NULL) {
|
|
luaO_pushfstring(L,
|
|
"attempt to load a %s chunk (mode is " LUA_QS ")", x, mode);
|
|
luaD_throw(L, LUA_ERRSYNTAX);
|
|
}
|
|
}
|
|
|
|
|
|
static void f_parser (lua_State *L, void *ud) {
|
|
int i;
|
|
Closure *cl;
|
|
struct SParser *p = cast(struct SParser *, ud);
|
|
int c = zgetc(p->z); /* read first character */
|
|
lua_assert(c != LUA_SIGNATURE[0]); /* binary not supported */
|
|
checkmode(L, p->mode, "text");
|
|
cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
|
|
lua_assert(cl->l.nupvalues == cl->l.p->sizeupvalues);
|
|
for (i = 0; i < cl->l.nupvalues; i++) { /* initialize upvalues */
|
|
UpVal *up = luaF_newupval(L);
|
|
cl->l.upvals[i] = up;
|
|
luaC_objbarrier(L, cl, up);
|
|
}
|
|
}
|
|
|
|
|
|
int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
|
|
const char *mode) {
|
|
struct SParser p;
|
|
int status;
|
|
L->nny++; /* cannot yield during parsing */
|
|
p.z = z; p.name = name; p.mode = mode;
|
|
p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0;
|
|
p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
|
|
p.dyd.label.arr = NULL; p.dyd.label.size = 0;
|
|
luaZ_initbuffer(L, &p.buff);
|
|
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
|
|
luaZ_freebuffer(L, &p.buff);
|
|
luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
|
|
luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
|
|
luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size);
|
|
L->nny--;
|
|
return status;
|
|
}
|