【发布时间】:2010-05-10 00:01:52
【问题描述】:
几天前,我们的程序崩溃了。我在 lua 代码中发现了崩溃。所以我检查了lua代码,发现堆栈溢出。
请在函数luaD_precall中查看此代码:
1 if (!cl->isC) { /* Lua function? prepare its call */
2 CallInfo *ci;
3 StkId st, base;
4 Proto *p = cl->p;
5 luaD_checkstack(L, p->maxstacksize);
6 func = restorestack(L, funcr);
7 if (!p->is_vararg) { /* no varargs? */
8 base = func + 1;
9 if (L->top > base + p->numparams)
10 L->top = base + p->numparams;
11 }
12 else { /* vararg function */
13 int nargs = cast_int(L->top - func) - 1;
14 base = adjust_varargs(L, p, nargs);
15 func = restorestack(L, funcr); /* previous call may change the stack */
16 }
17 ci = inc_ci(L); /* now `enter' new function */
18 ci->func = func;
19 L->base = ci->base = base;
20 ci->top = L->base + p->maxstacksize;
21 lua_assert(ci->top <= L->stack_last);
22 L->savedpc = p->code; /* starting point */
23 ci->tailcalls = 0;
24 ci->nresults = nresults;
25 for (st = L->top; st < ci->top; st++)
26 setnilvalue(st);
27 L->top = ci->top;
在我的程序中,p->maxstacksize 在第 5 行之前为 79,当前堆栈大小为 51,调用 luaD_checkstack 后,堆栈大小增长到 130。
lua 函数使用 vararg,所以会运行到第 14 行。函数adjust_varargs 将被调用。
static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
int i;
int nfixargs = p->numparams;
Table *htab = NULL;
StkId base, fixed;
for (; actual < nfixargs; ++actual)
setnilvalue(L->top++);
#if defined(LUA_COMPAT_VARARG)
if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */
int nvar = actual - nfixargs; /* number of extra arguments */
lua_assert(p->is_vararg & VARARG_HASARG);
luaC_checkGC(L);
htab = luaH_new(L, nvar, 1); /* create `arg' table */
在adjust_varargs()函数中,lua函数使用“arg”,所以luaC_checkGC会被调用。在luaC_checkGC 中,当前的 lua 堆栈大小将减少到 65!
调用栈是这样的:
但是p->maxstacksize是79,stacksize不够……
当程序运行到第27行时,L->top大于L->stack_last,在接下来的操作中会导致crash!
【问题讨论】:
-
触发此崩溃的用户 C/Lua 代码是什么样的?
-
如果您不发布您为创建此类场景而编写的代码,则无法判断您是否发现了错误。
-
当 L->top 大于 L->stack_last 时,在我的脚本中,下一个操作 new 在堆栈上创建一个闭包,然后,堆栈增长,此操作会将旧堆栈复制到新栈,从 L->stack 到 L->stack_last,所以闭包不会被复制。现在闭包没有初始化。当调用闭包时,会发生非法内存访问,然后程序崩溃!