« 树结构的一点想法 | 返回首页 | XOR 链表 »

Lua 5.2.2 中的一处 Bug

前几天, Lua 5.2.2 发布了, 主要是修复了 4 个 Lua 5.2.1 中已知的 bug . 其中包括前段时间一个同学和我在 email 交流中讨论的一个问题.

我把 Lua 5.2.2 更新到公司项目的主干上,同时需要对我的那本 《Lua 源码欣赏》做一些更新,需要把这次的代码更改同步到书里去。这个工作很繁琐,但有它的价值。比如我发现了 Lua 5.2.2 比 5.2.1 的更改远不只官方宣布了 4 处 bugfix ,还有一些小调整,让 Lua 的源码更规整一些。

阿楠同学因为这段时间一直在维护 UniLua 这个 C# 版的 Lua 项目,我就随便和他通告了一下这次的一些代码变更,方便他同步到 UniLua 项目中去。

讨论之中,他提到 luaD_precall 函数的实现有些诡异之处,没有看明白。我顺着他指出的位置又仔细阅读了一下,果然发现这里存在一个隐藏很深的 Bug 。

准确说,这不是 Lua 5.2 引入的新 Bug , 至少在 Lua 5.1 年代就存在了。只不过很难触碰到触发条件。

理解了代码后,我构造了一小段 Lua 代码,可以让 Bug 暴露出来。

function f(p1,p2,p3,p4,p5,p6,p7,p8,p9,...)
  local a1,a2,a3,a4,a5,a6,a7
  local a8,a9,a10,a11,a12,a13,a14
end

f()

运行这段 Lua 代码会让 Lua 虚拟机崩溃。我的修复 patch 如下:

diff --git a/src/ldo.c b/src/ldo.c
index aafa3dc..a901e6c 100644
--- a/src/ldo.c
+++ b/src/ldo.c
@@ -324,7 +324,7 @@ int luaD_precall (lua_State *L, StkId func, int nresults) {
     case LUA_TLCL: {  /* Lua function: prepare its call */
       StkId base;
       Proto *p = clLvalue(func)->p;
-      luaD_checkstack(L, p->maxstacksize);
+      luaD_checkstack(L, p->maxstacksize + p->numparams);
       func = restorestack(L, funcr);
       n = cast_int(L->top - func) - 1;  /* number of real arguments */
       for (; n < p->numparams; n++)

已经提交到 lua mailling list 里去了。Bug 的成因是:在 Lua 函数执行时,先按编译时统计出来的需要的寄存器个数扩展了堆栈。但是,如果函数有不定个数的参数,且调用者没有给完固定参数个数,会触发一个边界条件复制传入参数,结果是让预留的栈空间有可能不够。


4 月 18 日补充:

由于这是因为内存写越界造成的 bug , 所以是否会立即导致程序崩溃和编译环境有关.

我使用的是 mingw32 ,且打开了 -g 选项。

如果想确保看到问题,可以在 luaconf.h 里加上

#include "assert.h"
#define lua_assert assert

这样就会触发定义好的 assert 条件。


4 月 19 日:

这个 bug 终于在 lua mailling list 上被 Roberto 确认了. 英文不好是个问题, 前后写了好几篇才说清楚.

不过这个修复方案还需要斟酌. 因为可以有更好的方法去掉那次加法带来的额外开销.

"we can avoid this little overhead in the common case (non-vararg functions). We can either add p->numparams in the parser (so that the final maxstacksize of vararg functions already reflect this extra need) or check for numparams inside adjust_varargs, only for vararg functions. "

Comments

这么巧,这bug以前我提过
http://lua-users.org/lists/lua-l/2010-05/msg00136.html

踩踩~~~

我也打算学了

lua书的链接有问题

@pig09 应该是栈已经被你推高了,刚测试确实崩溃了。

大神。lua5.1.3。jit2.0.0beta8均未重现。是不是我的姿势不对?

老兄不覺得 lua 的性能太低了吗? 我觉得还是 luajit 性能高些.

《Lua 源码欣赏》的链接地址有问题

Post a comment

非这个主题相关的留言请到:留言本