Lua Bugs

5.1.3 · 5.1.2 · 5.1.1 · 5.1 · 5.0.3 · 5.0.2 · 5.0 · 4.0

Here is a list of all bugs found in each release of Lua since 4.0. Each entry includes a minimal example that exhibits the bug and a patch, when available.

Lua is never released with any pending bugs. Every Lua release fixes all known bugs in previous releases. (On the other hand, some bugs found in recent releases actually exist since older releases.)

If you want to report a bug, please read this first.

Lua 5.1.3 1 ·  2 ·  3 ·  4 ·  5 ·  6 ·  7 ·  8 ·  patch [NEW]

  1. LUAI_MAXCSTACK must be smaller than -LUA_REGISTRYINDEX.
    reported by Patrick Donnelly on 11 Feb 2008.

    Example:

    j = 1e4
    co = coroutine.create(function()
           t = {}
           for i = 1, j do t[i] = i end
           return unpack(t)
    end)
    print(coroutine.resume(co))
    

    Patch:

    luaconf.h:
    443c443,444
    < ** functions to consume unlimited stack space.
    ---
    > ** functions to consume unlimited stack space. (must be smaller than
    > ** -LUA_REGISTRYINDEX)
    445,446c446
    < #define LUAI_MCS_AUX  ((int)(INT_MAX / (4*sizeof(LUA_NUMBER))))
    < #define LUAI_MAXCSTACK        (LUAI_MCS_AUX > SHRT_MAX ? SHRT_MAX : LUAI_MCS_AUX)
    ---
    > #define LUAI_MAXCSTACK        8000
    


  2. coroutine.resume pushes element without ensuring stack size.
    reported on 11 Feb 2008.

    Example: this bug cannot be detected without internal assertions.

    Patch:

    lbaselib.c:
    @@ -526,7 +526,7 @@
       status = lua_resume(co, narg);
       if (status == 0 || status == LUA_YIELD) {
         int nres = lua_gettop(co);
    -    if (!lua_checkstack(L, nres))
    +    if (!lua_checkstack(L, nres + 1))
           luaL_error(L, "too many results to resume");
         lua_xmove(co, L, nres);  /* move yielded values */
         return nres;
    


  3. lua_checkstack may have arithmetic overflow for large 'size'.
    reported by Patrick Donnelly on 12 Feb 2008.

    Example:

    print(unpack({1,2,3}, 0, 2^31-3))
    

    Patch:

    lapi.c:
    @@ -93,15 +93,14 @@
     
     
     LUA_API int lua_checkstack (lua_State *L, int size) {
    -  int res;
    +  int res = 1;
       lua_lock(L);
    -  if ((L->top - L->base + size) > LUAI_MAXCSTACK)
    +  if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK)
         res = 0;  /* stack overflow */
    -  else {
    +  else if (size > 0) {
         luaD_checkstack(L, size);
         if (L->ci->top < L->top + size)
           L->ci->top = L->top + size;
    -    res = 1;
       }
       lua_unlock(L);
       return res;
    


  4. unpack with maximum indices may crash due to arithmetic overflow.
    reported by Patrick Donnelly on 12 Feb 2008.

    Example:

    print(unpack({1,2,3}, 2^31-1, 2^31-1))
    

    Patch:

    lbaselib.c:
    @@ -344,10 +344,12 @@
       luaL_checktype(L, 1, LUA_TTABLE);
       i = luaL_optint(L, 2, 1);
       e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));
    +  if (i > e) return 0;  /* empty range */
       n = e - i + 1;  /* number of elements */
    -  if (n <= 0) return 0;  /* empty range */
    -  luaL_checkstack(L, n, "table too big to unpack");
    -  for (; i<=e; i++)  /* push arg[i...e] */
    +  if (n <= 0 || !lua_checkstack(L, n))  /* n <= 0 means arith. overflow */
    +    return luaL_error(L, "too many results to unpack");
    +  lua_rawgeti(L, 1, i);  /* push arg[i] (avoiding overflow problems) */
    +  while (i++ < e)  /* push arg[i + 1...e] */
         lua_rawgeti(L, 1, i);
       return n;
     }
    


  5. The validator for precompiled code has several flaws that allow malicious binary code to crash the application.
    reported by Peter Cawley on 24 Mar 2008.

    Example:

    a = string.dump(function()return;end)
    a = a:gsub(string.char(30,37,122,128), string.char(34,0,0), 1)
    loadstring(a)()
    

    Patch:

    ldebug.c:
    @@ -275,12 +275,12 @@
     
     static int precheck (const Proto *pt) {
       check(pt->maxstacksize <= MAXSTACK);
    -  lua_assert(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);
    -  lua_assert(!(pt->is_vararg & VARARG_NEEDSARG) ||
    +  check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);
    +  check(!(pt->is_vararg & VARARG_NEEDSARG) ||
                   (pt->is_vararg & VARARG_HASARG));
       check(pt->sizeupvalues <= pt->nups);
       check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0);
    -  check(GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);
    +  check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);
       return 1;
     }
     
    @@ -363,7 +363,11 @@
         }
         switch (op) {
           case OP_LOADBOOL: {
    -        check(c == 0 || pc+2 < pt->sizecode);  /* check its jump */
    +        if (c == 1) {  /* does it jump? */
    +          check(pc+2 < pt->sizecode);  /* check its jump */
    +          check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST ||
    +                GETARG_C(pt->code[pc+1]) != 0);
    +        }
             break;
           }
           case OP_LOADNIL: {
    @@ -428,7 +432,10 @@
           }
           case OP_SETLIST: {
             if (b > 0) checkreg(pt, a + b);
    -        if (c == 0) pc++;
    +        if (c == 0) {
    +          pc++;
    +          check(pc < pt->sizecode - 1);
    +        }
             break;
           }
           case OP_CLOSURE: {
    


  6. Maliciously crafted precompiled code can blow the C stack.
    reported by Greg Falcon on 25 Mar 2008.

    Example:

    function crash(depth)
      local init = '\27\76\117\97\81\0\1\4\4\4\8\0\7\0\0\0\61\115\116' ..
                   '\100\105\110\0\1\0\0\0\1\0\0\0\0\0\0\2\2\0\0\0\36' ..
                   '\0\0\0\30\0\128\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0' ..
                   '\1\0\0\0\0\0\0\2'
      local mid = '\1\0\0\0\30\0\128\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\0'
      local fin = '\0\0\0\0\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2\0' ..
                  '\0\0\97\0\1\0\0\0\1\0\0\0\0\0\0\0'
      local lch = '\2\0\0\0\36\0\0\0\30\0\128\0\0\0\0\0\1\0\0\0\0\0\0' ..
                  '\0\1\0\0\0\1\0\0\0\0\0\0\2'
      local rch = '\0\0\0\0\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2\0' ..
                  '\0\0\97\0\1\0\0\0\1'
      for i=1,depth do lch,rch = lch..lch,rch..rch end
      loadstring(init .. lch .. mid .. rch .. fin)
    end
    for i=1,25 do print(i); crash(i) end
    

    Patch:

    lundump.c:
    @@ -161,7 +160,9 @@
     
     static Proto* LoadFunction(LoadState* S, TString* p)
     {
    - Proto* f=luaF_newproto(S->L);
    + Proto* f;
    + if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,"code too deep");
    + f=luaF_newproto(S->L);
      setptvalue2s(S->L,S->L->top,f); incr_top(S->L);
      f->source=LoadString(S); if (f->source==NULL) f->source=p;
      f->linedefined=LoadInt(S);
    @@ -175,6 +176,7 @@
      LoadDebug(S,f);
      IF (!luaG_checkcode(f), "bad code");
      S->L->top--;
    + S->L->nCcalls--;
      return f;
     }
     
    


  7. Code validator may reject (maliciously crafted) correct code.
    reported by Greg Falcon on 26 Mar 2008.

    Example:

    z={}
    for i=1,27290 do z[i]='1,' end
    z = 'if 1+1==2 then local a={' .. table.concat(z) .. '} end'
    func = loadstring(z)
    print(loadstring(string.dump(func)))
    

    Patch:

    ldebug.c:
    @@ -346,9 +346,18 @@
               int dest = pc+1+b;
               check(0 <= dest && dest < pt->sizecode);
               if (dest > 0) {
    -            /* cannot jump to a setlist count */
    -            Instruction d = pt->code[dest-1];
    -            check(!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0));
    +            int j;
    +            /* check that it does not jump to a setlist count; this
    +               is tricky, because the count from a previous setlist may
    +               have the same value of an invalid setlist; so, we must
    +               go all the way back to the first of them (if any) */
    +            for (j = 0; j < dest; j++) {
    +              Instruction d = pt->code[dest-1-j];
    +              if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break;
    +            }
    +            /* if 'j' is even, previous value is not a setlist (even if
    +               it looks like one) */
    +            check((j&1) == 0);
               }
             }
             break;
    


  8. Maliciously crafted precompiled code can inject invalid boolean values into Lua code.
    reported by Greg Falcon on 27 Mar 2008.

    Example:

    maybe = string.dump(function() return ({[true]=true})[true] end)
    maybe = maybe:gsub('\1\1','\1\2')
    maybe = loadstring(maybe)()
    assert(type(maybe) == "boolean" and maybe ~= true and maybe ~= false)
    

    Patch:

    lundump.c:
    @@ -115,7 +115,7 @@
            setnilvalue(o);
            break;
        case LUA_TBOOLEAN:
    -       setbvalue(o,LoadChar(S));
    +       setbvalue(o,LoadChar(S)!=0);
            break;
        case LUA_TNUMBER:
            setnvalue(o,LoadNumber(S));
    


Lua 5.1.2 1 ·  2 ·  3 ·  4 ·  5 ·  6 ·  7 ·  8 ·  9 ·  10 ·  11 ·  12 ·  13

  1. Lua may close standard files, which then may be used by C.
    reported by David Manura on 17 Apr 2007. Fixed in 5.1.3.


  2. Code generated for "-nil", "-true", and "-false" is wrong.
    reported by David Manura and Rici Lake on 29 Apr 2007. Fixed in 5.1.3.

    Example:

    print(-nil)

    Patch:

    lcode.c:
    @@ -699,7 +699,7 @@
       e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0;
       switch (op) {
         case OPR_MINUS: {
    -      if (e->k == VK)
    +      if (!isnumeral(e))
             luaK_exp2anyreg(fs, e);  /* cannot operate on non-numeric constants */
           codearith(fs, OP_UNM, e, &e2);
           break;
    


  3. Count hook may be called without being set.
    reported by Mike Pall in May 2007. Fixed in 5.1.3.

    Patch:

    lvm.c:
    @@ -61,11 +61,9 @@
       lu_byte mask = L->hookmask;
       const Instruction *oldpc = L->savedpc;
       L->savedpc = pc;
    -  if (mask > LUA_MASKLINE) {  /* instruction-hook set? */
    -    if (L->hookcount == 0) {
    -      resethookcount(L);
    -      luaD_callhook(L, LUA_HOOKCOUNT, -1);
    -    }
    +  if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) {
    +    resethookcount(L);
    +    luaD_callhook(L, LUA_HOOKCOUNT, -1);
       }
       if (mask & LUA_MASKLINE) {
         Proto *p = ci_func(L->ci)->l.p;
    


  4. Recursive coroutines may overflow C stack.

    Example:

    a = function(a) coroutine.wrap(a)(a) end
    a(a)
    

    Patch: The 'nCcalls' counter should be shared by all threads. (That is, it should be declared in the 'global_State' structure, not in 'lua_State'.)


  5. Wrong error message in some concatenations.
    reported by Alex Davies in May 2007. Fixed in 5.1.3.

    Example:

    a = nil; a = (1)..a

    Patch:

    ldebug.c:
    @@ -565,8 +565,8 @@
    
    
     void luaG_concaterror (lua_State *L, StkId p1, StkId p2) {
    -  if (ttisstring(p1)) p1 = p2;
    -  lua_assert(!ttisstring(p1));
    +  if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;
    +  lua_assert(!ttisstring(p1) && !ttisnumber(p1));
       luaG_typeerror(L, p1, "concatenate");
     }
    
    


  6. Very small numbers all collide in the hash function. (This creates only performance problems; the behavior is correct.).
    reported on 18 Apr 2007. Fixed in 5.1.3.

    Patch:

    ltable.c:
    87,88c87,88
    <   n += 1;  /* normalize number (avoid -0) */
    <   lua_assert(sizeof(a) <= sizeof(n));
    ---
    >   if (luai_numeq(n, 0))  /* avoid problems with -0 */
    >     return gnode(t, 0);
    


  7. Too many variables in an assignment may cause a C stack overflow.
    reported by Mike Pall on 31 Jul 2007. Fixed in 5.1.3.

    Example:

    $ ulimit -s 1024       # Reduce C stack to 1MB for quicker results
    $ lua -e 'local s = "a,"; for i=1,18 do s = s..s end print(loadstring("local a;"..s.."a=nil", ""))'
    

    Patch:

    lparser.c:
    @@ -938,6 +938,8 @@
         primaryexp(ls, &nv.v);
         if (nv.v.k == VLOCAL)
           check_conflict(ls, lh, &nv.v);
    +    luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls,
    +                    "variable names");
         assignment(ls, &nv, nvars+1);
       }
       else {  /* assignment -> `=' explist1 */
    


  8. An error in a module loaded through the '-l' option shows no traceback.
    reported by David Manura on 25 Aug 2007. Fixed in 5.1.3.

    Example:

    lua -ltemp    (assuming temp.lua has an error)

    Patch:

    lua.c:
    @@ -144,7 +144,7 @@
     static int dolibrary (lua_State *L, const char *name) {
       lua_getglobal(L, "require");
       lua_pushstring(L, name);
    -  return report(L, lua_pcall(L, 1, 0, 0));
    +  return report(L, docall(L, 1, 1));
     }
    


  9. gsub may go wild when wrongly called without its third argument and with a large subject.
    reported by Florian Berger on 26 Oct 2007. Fixed in 5.1.3.

    Example:

    x = string.rep('a', 10000) .. string.rep('b', 10000)
    print(#string.gsub(x, 'b'))
    

    Patch:

    lstrlib.c:
    @@ -631,6 +631,2 @@
         }
    -    default: {
    -      luaL_argerror(L, 3, "string/function/table expected"); 
    -      return;
    -    }
       }
    @@ -650,2 +646,3 @@
       const char *p = luaL_checkstring(L, 2);
    +  int  tr = lua_type(L, 3);
       int max_s = luaL_optint(L, 4, srcl+1);
    @@ -655,2 +652,5 @@
       luaL_Buffer b;
    +  luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
    +                   tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
    +                      "string/function/table expected");
       luaL_buffinit(L, &b);
    


  10. table.remove removes last element of a table when given an out-of-bound index.
    reported by Patrick Donnelly on 13 Nov 2007. Fixed in 5.1.3.

    Example:

    a = {1,2,3}
    table.remove(a, 4)
    print(a[3])   --> nil   (should be 3)
    

    Patch:

    ltablib.c:
    @@ -118,7 +118,8 @@
     static int tremove (lua_State *L) {
       int e = aux_getn(L, 1);
       int pos = luaL_optint(L, 2, e);
    -  if (e == 0) return 0;  /* table is `empty' */
    +  if (!(1 <= pos && pos <= e))  /* position is outside bounds? */
    +   return 0;  /* nothing to remove */
       luaL_setn(L, 1, e - 1);  /* t.n = n-1 */
       lua_rawgeti(L, 1, pos);  /* result = t[pos] */
       for ( ;pos
    


  11. lua_setfenv may crash if called over an invalid object.
    reported by Mike Pall on 28 Nov 2007. Fixed in 5.1.3.

    Example:

    > debug.setfenv(3, {})
    

    Patch:

    lapi.c:
    @@ -749,7 +749,7 @@
           res = 0;
           break;
       }
    -  luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1));
    +  if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1));
       L->top--;
       lua_unlock(L);
       return res;
    


  12. Stand-alone interpreter shows incorrect error message when the "message" is a coroutine.
    reported by Patrick Donnelly on 17 Dec 2007. Fixed in 5.1.3.

    Example:

    > error(coroutine.create(function() end))
    

    Patch:

    lua.c:
    @@ -74,6 +74,8 @@
     
     
     static int traceback (lua_State *L) {
    +  if (!lua_isstring(L, 1))  /* 'message' not a string? */
    +    return 1;  /* keep it intact */
       lua_getfield(L, LUA_GLOBALSINDEX, "debug");
       if (!lua_istable(L, -1)) {
         lua_pop(L, 1);
    
    


  13. debug.sethook/gethook may overflow the thread's stack.
    reported by Ivko Stanilov on 04 Jan 2008. Fixed in 5.1.3.

    Example:

    a = coroutine.create(function() yield() end) 
    coroutine.resume(a)
    debug.sethook(a)      -- may overflow the stack of 'a'
    

    Patch:

    ldblib.c:
    @@ -268,12 +268,11 @@
         count = luaL_optint(L, arg+3, 0);
         func = hookf; mask = makemask(smask, count);
       }
    -  gethooktable(L1);
    -  lua_pushlightuserdata(L1, L1);
    +  gethooktable(L);
    +  lua_pushlightuserdata(L, L1);
       lua_pushvalue(L, arg+1);
    -  lua_xmove(L, L1, 1);
    -  lua_rawset(L1, -3);  /* set new hook */
    -  lua_pop(L1, 1);  /* remove hook table */
    +  lua_rawset(L, -3);  /* set new hook */
    +  lua_pop(L, 1);  /* remove hook table */
       lua_sethook(L1, func, mask, count);  /* set hooks */
       return 0;
     }
    @@ -288,11 +287,10 @@
       if (hook != NULL && hook != hookf)  /* external hook? */
         lua_pushliteral(L, "external hook");
       else {
    -    gethooktable(L1);
    -    lua_pushlightuserdata(L1, L1);
    -    lua_rawget(L1, -2);   /* get hook */
    -    lua_remove(L1, -2);  /* remove hook table */
    -    lua_xmove(L1, L, 1);
    +    gethooktable(L);
    +    lua_pushlightuserdata(L, L1);
    +    lua_rawget(L, -2);   /* get hook */
    +    lua_remove(L, -2);  /* remove hook table */
       }
       lua_pushstring(L, unmakemask(mask, buff));
       lua_pushinteger(L, lua_gethookcount(L1));
    


Lua 5.1.1 1 ·  2 ·  3 ·  4 ·  5 ·  6 ·  7 ·  8 ·  9

  1. List constructors have wrong limit.
    reported by Norman Ramsey on 5 Jun 2006. Fixed in 5.1.2.

    Example:

    a = {}
    a[1] = "x={1"
    for i = 2, 2^20 do
      a[i] = 1
    end
    a[#a + 1] = "}"
    s = table.concat(a, ",")
    assert(loadstring(s))()
    print(#x)
    

    Patch:

    lparser.c:
    @@ -489,7 +489,7 @@
    
     static void listfield (LexState *ls, struct ConsControl *cc) {
       expr(ls, &cc->v);
    -  luaY_checklimit(ls->fs, cc->na, MAXARG_Bx, "items in a constructor");
    +  luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor");
       cc->na++;
       cc->tostore++;
     }
    


  2. Wrong message error in some cases involving closures.
    reported by Shmuel Zeigerman on 8 Jul 2006. Fixed in 5.1.2.

    Example:

    local Var
    local function main()
      NoSuchName (function() Var=0 end)
    end
    main()
    
    The error message is attempt to call upvalue 'Var' (a nil value). It should be attempt to call global 'NoSuchName' (a nil value).

    Patch:

    ldebug.c:
    @@ -435,14 +435,16 @@
             break;
           }
           case OP_CLOSURE: {
    -        int nup;
    +        int nup, j;
             check(b < pt->sizep);
             nup = pt->p[b]->nups;
             check(pc + nup < pt->sizecode);
    -        for (; nup>0; nup--) {
    -          OpCode op1 = GET_OPCODE(pt->code[pc+nup]);
    +        for (j = 1; j <= nup; j++) {
    +          OpCode op1 = GET_OPCODE(pt->code[pc + j]);
               check(op1 == OP_GETUPVAL || op1 == OP_MOVE);
             }
    +        if (reg != NO_REG)  /* tracing? */
    +          pc += nup;  /* do not 'execute' these pseudo-instructions */
             break;
           }
           case OP_VARARG: {
    


  3. string.format("%") may read past the string.
    reported by Roberto in Sep 2006. Fixed in 5.1.2.

    Example:

    print(string.format("%"))
    

    Patch:

    lstrlib.c:
    @@ -723,7 +723,7 @@
    
     static const char *scanformat (lua_State *L, const char *strfrmt, char *form) {
       const char *p = strfrmt;
    -  while (strchr(FLAGS, *p)) p++;  /* skip flags */
    +  while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++;  /* skip flags */
       if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
         luaL_error(L, "invalid format (repeated flags)");
       if (isdigit(uchar(*p))) p++;  /* skip width */
    


  4. os.date throws an error when result is the empty string.
    reported by Nick Gammon on 15 Sep 2006. Fixed in 5.1.2.

    Example:

    print(os.date(""))

    Patch:

    loslib.c:
    @@ -148,7 +148,18 @@
       else {
    -    char b[256];
    -    if (strftime(b, sizeof(b), s, stm))
    -      lua_pushstring(L, b);
    -    else
    -      return luaL_error(L, LUA_QL("date") " format too long");
    +    char cc[3];
    +    luaL_Buffer b;
    +    cc[0] = '%'; cc[2] = '\0';
    +    luaL_buffinit(L, &b);
    +    for (; *s; s++) {
    +      if (*s != '%' || *(s + 1) == '\0')  /* no conversion specifier? */
    +        luaL_addchar(&b, *s);
    +      else {
    +        size_t reslen;
    +        char buff[200];  /* should be big enough for any conversion result */
    +        cc[1] = *(++s);
    +        reslen = strftime(buff, sizeof(buff), cc, stm);
    +        luaL_addlstring(&b, buff, reslen);
    +      }
    +    }
    +    luaL_pushresult(&b);
       }
    


  5. setfenv accepts invalid first argument.
    reported by Doug Rogers in 8 Feb 2007. Fixed in 5.1.2.

    Example:

    setfenv(nil, {})   -- should throw an error

    Patch:

    lbaselib.c:
    @@ -116,3 +116,3 @@
    
    -static void getfunc (lua_State *L) {
    +static void getfunc (lua_State *L, int opt) {
       if (lua_isfunction(L, 1)) lua_pushvalue(L, 1);
    @@ -120,3 +120,3 @@
         lua_Debug ar;
    -    int level = luaL_optint(L, 1, 1);
    +    int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1);
         luaL_argcheck(L, level >= 0, 1, "level must be non-negative");
    @@ -133,3 +133,3 @@
     static int luaB_getfenv (lua_State *L) {
    -  getfunc(L);
    +  getfunc(L, 1);
       if (lua_iscfunction(L, -1))  /* is a C function? */
    @@ -144,3 +144,3 @@
       luaL_checktype(L, 2, LUA_TTABLE);
    -  getfunc(L);
    +  getfunc(L, 0);
       lua_pushvalue(L, 2);
    


  6. Wrong code generated for arithmetic expressions in some specific scenarios.
    reported by Thierry Grellier on 19 Jan 2007. Fixed in 5.1.2.

    Example:

    -- use a large number of names (almost 256)
    v1=1; v2=1; v3=1; v4=1; v5=1; v6=1; v7=1; v8=1; v9=1;
    v10=1; v11=1; v12=1; v13=1; v14=1; v15=1; v16=1; v17=1;
    v18=1; v19=1; v20=1; v21=1; v22=1; v23=1; v24=1; v25=1;
    v26=1; v27=1; v28=1; v29=1; v30=1; v31=1; v32=1; v33=1;
    v34=1; v35=1; v36=1; v37=1; v38=1; v39=1; v40=1; v41=1;
    v42=1; v43=1; v44=1; v45=1; v46=1; v47=1; v48=1; v49=1;
    v50=1; v51=1; v52=1; v53=1; v54=1; v55=1; v56=1; v57=1;
    v58=1; v59=1; v60=1; v61=1; v62=1; v63=1; v64=1; v65=1;
    v66=1; v67=1; v68=1; v69=1; v70=1; v71=1; v72=1; v73=1;
    v74=1; v75=1; v76=1; v77=1; v78=1; v79=1; v80=1; v81=1;
    v82=1; v83=1; v84=1; v85=1; v86=1; v87=1; v88=1; v89=1;
    v90=1; v91=1; v92=1; v93=1; v94=1; v95=1; v96=1; v97=1;
    v98=1; v99=1; v100=1; v101=1; v102=1; v103=1; v104=1; v105=1;
    v106=1; v107=1; v108=1; v109=1; v110=1; v111=1; v112=1; v113=1;
    v114=1; v115=1; v116=1; v117=1; v118=1; v119=1; v120=1; v121=1;
    v122=1; v123=1; v124=1; v125=1; v126=1; v127=1; v128=1; v129=1;
    v130=1; v131=1; v132=1; v133=1; v134=1; v135=1; v136=1; v137=1;
    v138=1; v139=1; v140=1; v141=1; v142=1; v143=1; v144=1; v145=1;
    v146=1; v147=1; v148=1; v149=1; v150=1; v151=1; v152=1; v153=1;
    v154=1; v155=1; v156=1; v157=1; v158=1; v159=1; v160=1; v161=1;
    v162=1; v163=1; v164=1; v165=1; v166=1; v167=1; v168=1; v169=1;
    v170=1; v171=1; v172=1; v173=1; v174=1; v175=1; v176=1; v177=1;
    v178=1; v179=1; v180=1; v181=1; v182=1; v183=1; v184=1; v185=1;
    v186=1; v187=1; v188=1; v189=1; v190=1; v191=1; v192=1; v193=1;
    v194=1; v195=1; v196=1; v197=1; v198=1; v199=1; v200=1; v201=1;
    v202=1; v203=1; v204=1; v205=1; v206=1; v207=1; v208=1; v209=1;
    v210=1; v211=1; v212=1; v213=1; v214=1; v215=1; v216=1; v217=1;
    v218=1; v219=1; v220=1; v221=1; v222=1; v223=1; v224=1; v225=1;
    v226=1; v227=1; v228=1; v229=1; v230=1; v231=1; v232=1; v233=1;
    v234=1; v235=1; v236=1; v237=1; v238=1; v239=1; v240=1; v241=1;
    v242=1; v243=1; v244=1; v245=1; v246=1; v247=1; v248=1; v249=1;
    v250=1;
    v251={k1 = 1};
    v252=1;
    print(2 * v251.k1, v251.k1 * 2);   -- 2 2, OK
    v253=1;
    print(2 * v251.k1, v251.k1 * 2);   -- 1 2, ???
    

    Patch:

    lcode.c:
    @@ -657,10 +657,16 @@
       if (constfolding(op, e1, e2))
         return;
       else {
    -    int o1 = luaK_exp2RK(fs, e1);
         int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0;
    -    freeexp(fs, e2);
    -    freeexp(fs, e1);
    +    int o1 = luaK_exp2RK(fs, e1);
    +    if (o1 > o2) {
    +      freeexp(fs, e1);
    +      freeexp(fs, e2);
    +    }
    +    else {
    +      freeexp(fs, e2);
    +      freeexp(fs, e1);
    +    }
         e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2);
         e1->k = VRELOCABLE;
       }
    @@ -718,10 +724,15 @@
           luaK_exp2nextreg(fs, v);  /* operand must be on the `stack' */
           break;
         }
    -    default: {
    +    case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
    +    case OPR_MOD: case OPR_POW: {
           if (!isnumeral(v)) luaK_exp2RK(fs, v);
           break;
         }
    +    default: {
    +      luaK_exp2RK(fs, v);
    +      break;
    +    }
       }
     }
    


  7. Assignment of nil to parameter may be optimized away.
    reported by Thomas Lauer on 21 Mar 2007. Fixed in 5.1.2.

    Example:

    function f (a)
      a=nil
      return a
    end
    
    print(f("test"))
    

    Patch:

    lcode.c:
    @@ -35,16 +35,20 @@
     void luaK_nil (FuncState *fs, int from, int n) {
       Instruction *previous;
       if (fs->pc > fs->lasttarget) {  /* no jumps to current position? */
    -    if (fs->pc == 0)  /* function start? */
    -      return;  /* positions are already clean */
    -    previous = &fs->f->code[fs->pc-1];
    -    if (GET_OPCODE(*previous) == OP_LOADNIL) {
    -      int pfrom = GETARG_A(*previous);
    -      int pto = GETARG_B(*previous);
    -      if (pfrom <= from && from <= pto+1) {  /* can connect both? */
    -        if (from+n-1 > pto)
    -          SETARG_B(*previous, from+n-1);
    -        return;
    +    if (fs->pc == 0) {  /* function start? */
    +      if (from >= fs->nactvar)
    +        return;  /* positions are already clean */
    +    }
    +    else {
    +      previous = &fs->f->code[fs->pc-1];
    +      if (GET_OPCODE(*previous) == OP_LOADNIL) {
    +        int pfrom = GETARG_A(*previous);
    +        int pto = GETARG_B(*previous);
    +        if (pfrom <= from && from <= pto+1) {  /* can connect both? */
    +          if (from+n-1 > pto)
    +            SETARG_B(*previous, from+n-1);
    +          return;
    +        }
           }
         }
       }
    


  8. __concat metamethod converts numbers to strings.
    reported by Paul Winwood on 24 Dec 2006. Fixed in 5.1.2.

    Example:

    a = {}
    setmetatable(a, {__concat = function (a,b) print(type(a), type(b)) end})
    a = 4 .. a
    

    Patch:

    lvm.c:
    @@ -281,10 +281,12 @@
       do {
         StkId top = L->base + last + 1;
         int n = 2;  /* number of elements handled in this pass (at least 2) */
    -    if (!tostring(L, top-2) || !tostring(L, top-1)) {
    +    if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) {
           if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT))
             luaG_concaterror(L, top-2, top-1);
    -    } else if (tsvalue(top-1)->len > 0) {  /* if len=0, do nothing */
    +    } else if (tsvalue(top-1)->len == 0)  /* second op is empty? */
    +      (void)tostring(L, top - 2);  /* result is first op (as string) */
    +    else {
           /* at least two string values; get as many as possible */
           size_t tl = tsvalue(top-1)->len;
           char *buffer;
    


  9. loadlib.c is a library and should not access Lua internals (via lobject.h).
    reported by Jérôme Vuarand on 25 Mar 2007. Fixed in 5.1.2.

    Example: the bug has no effect on external behavior.

    Patch: In loadlib.c, change all ocurrences of luaO_pushfstring to lua_pushfstring.


Lua 5.1 1 ·  2 ·  3 ·  4 ·  5 ·  6 ·  7 ·  8 ·  9 ·  10

  1. In 16-bit machines, and/or expressions with numeric constants as the right operand may result in weird values.
    reported by Andreas Stenius on 15 Mar 2006. Fixed in 5.1.1.

    Example:

    print(false or 0)   -- on 16-bit machines
    

    Patch:

    lcode.c:
    @@ -731,17 +731,15 @@
         case OPR_AND: {
           lua_assert(e1->t == NO_JUMP);  /* list must be closed */
           luaK_dischargevars(fs, e2);
    -      luaK_concat(fs, &e1->f, e2->f);
    -      e1->k = e2->k; e1->u.s.info = e2->u.s.info;
    -      e1->u.s.aux = e2->u.s.aux; e1->t = e2->t;
    +      luaK_concat(fs, &e2->f, e1->f);
    +      *e1 = *e2;
           break;
         }
         case OPR_OR: {
           lua_assert(e1->f == NO_JUMP);  /* list must be closed */
           luaK_dischargevars(fs, e2);
    -      luaK_concat(fs, &e1->t, e2->t);
    -      e1->k = e2->k; e1->u.s.info = e2->u.s.info;
    -      e1->u.s.aux = e2->u.s.aux; e1->f = e2->f;
    +      luaK_concat(fs, &e2->t, e1->t);
    +      *e1 = *e2;
           break;
         }
    


  2. luaL_checkudata may produce wrong error message.
    reported by Greg Falcon on 21 Mar 2006. Fixed in 5.1.1.

    Example:

    getmetatable(io.stdin).__gc()
      --> bad argument #1 to '__gc' (FILE* expected, got table)
    

    Patch:

    lauxlib.c:
    @@ -123,11 +123,17 @@
    
     LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {
       void *p = lua_touserdata(L, ud);
    -  lua_getfield(L, LUA_REGISTRYINDEX, tname);  /* get correct metatable */
    -  if (p == NULL || !lua_getmetatable(L, ud) || !lua_rawequal(L, -1, -2))
    -    luaL_typerror(L, ud, tname);
    -  lua_pop(L, 2);  /* remove both metatables */
    -  return p;
    +  if (p != NULL) {  /* value is a userdata? */
    +    if (lua_getmetatable(L, ud)) {  /* does it have a metatable? */
    +      lua_getfield(L, LUA_REGISTRYINDEX, tname);  /* get correct metatable */
    +      if (lua_rawequal(L, -1, -2)) {  /* does it have the correct mt? */
    +        lua_pop(L, 2);  /* remove both metatables */
    +        return p;
    +      }
    +    }
    +  }
    +  luaL_typerror(L, ud, tname);  /* else error */
    +  return NULL;  /* to avoid warnings */
     }
    


  3. Windows applications that use both Lua and DirectX may present erractic behavior. THIS IS NOT A BUG IN LUA! The problem is that DirectX violates an ABI that Lua depends on.

    Patch: The simplest solution is to use DirectX with the D3DCREATE_FPU_PRESERVE flag. Otherwise, you can change the definition of lua_number2int in luaconf.h to this one:

    #define lua_number2int(i,d)   __asm fld d   __asm fistp i
    


  4. Option '%q' in string.format does not handle '\r' correctly.
    reported by FleetCommand on 1 Apr 2006. Fixed in 5.1.1.

    Example:

    local s = "a string with \r and \n and \r\n and \n\r"
    local c = string.format("return %q", s)
    assert(assert(loadstring(c))() == s)
    

    Patch:

    lstrlib.c:
    @@ -703,6 +703,10 @@
             luaL_addchar(b, *s);
             break;
           }
    +      case '\r': {
    +        luaL_addlstring(b, "\\r", 2);
    +        break;
    +      }
           case '\0': {
             luaL_addlstring(b, "\\000", 4);
             break;
    


  5. luaL_dofile and luaL_dostring should return all values returned by the chunk.
    reported by mos on 11 Apr 2006. Fixed in 5.1.1.

    Patch:

    lauxlib.h:
    @@ -108,9 +108,11 @@
    
     #define luaL_typename(L,i)     lua_typename(L, lua_type(L,(i)))
    
    -#define luaL_dofile(L, fn)     (luaL_loadfile(L, fn) || lua_pcall(L, 0, 0, 0))
    +#define luaL_dofile(L, fn) \
    +       (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
    
    -#define luaL_dostring(L, s)    (luaL_loadstring(L, s) || lua_pcall(L, 0, 0, 0))
    +#define luaL_dostring(L, s) \
    +       (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
    
     #define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
    


  6. Garbage collector does not compensate enough for finalizers.
    reported by Roberto in May 2006. Fixed in 5.1.1.

    Patch:

    lgc.c:
    @@ -322,4 +322,6 @@
    
    -static void propagateall (global_State *g) {
    -  while (g->gray) propagatemark(g);
    +static size_t propagateall (global_State *g) {
    +  size_t m = 0;
    +  while (g->gray) m += propagatemark(g);
    +  return m;
     }
    @@ -542,3 +544,3 @@
       marktmu(g);  /* mark `preserved' userdata */
    -  propagateall(g);  /* remark, to propagate `preserveness' */
    +  udsize += propagateall(g);  /* remark, to propagate `preserveness' */
       cleartable(g->weak);  /* remove collected objects from weak tables */
    @@ -592,2 +594,4 @@
             GCTM(L);
    +        if (g->estimate > GCFINALIZECOST)
    +          g->estimate -= GCFINALIZECOST;
    


  7. Debug hooks may get wrong when mixed with coroutines.
    reported by Ivko Stanilov on 3 Jun 2006. Fixed in 5.1.1.

    Example:

    co = coroutine.create(function () coroutine.yield() end)
    debug.sethook(co, function() end, "lr")
    coroutine.resume(co)
    coroutine.resume(co)
    

    Patch:

    ldo.c:
    @@ -389,6 +389,7 @@
           return;
       }
       else {  /* resuming from previous yield */
    +    L->status = 0;
         if (!f_isLua(ci)) {  /* `common' yield? */
           /* finish interrupted execution of `OP_CALL' */
           lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL ||
    @@ -399,7 +400,6 @@
         else  /* yielded inside a hook: just continue its execution */
           L->base = L->ci->base;
       }
    -  L->status = 0;
       luaV_execute(L, cast_int(L->ci - L->base_ci));
     }
    


  8. List constructors have wrong limit.

  9. Wrong message error in some cases involving closures.

  10. Wrong code generated for arithmetic expressions in some specific scenarios.

Lua 5.0.3

No bugs are known in Lua 5.0.3.

Lua 5.0.2 1 ·  2 ·  3 ·  4 ·  5 ·  6 ·  7 ·  8

  1. String concatenation may cause arithmetic overflow, leading to a buffer overflow.
    reported by Rici Lake on 20 May 2004. Fixed in 5.1 and 5.0.3.

    Example:

    longs = string.rep("\0", 2^25)
    function catter(i)
        return assert(loadstring(
          string.format("return function(a) return a%s end",
                         string.rep("..a", i-1))))()
    end
    rep129 = catter(129)
    rep129(longs)
    
    Patch:
    lvm.c:
    @@ -321,15 +321,15 @@
             luaG_concaterror(L, top-2, top-1);
         } else if (tsvalue(top-1)->tsv.len > 0) {  /* if len=0, do nothing */
           /* at least two string values; get as many as possible */
    -      lu_mem tl = cast(lu_mem, tsvalue(top-1)->tsv.len) +
    -                  cast(lu_mem, tsvalue(top-2)->tsv.len);
    +      size_t tl = tsvalue(top-1)->tsv.len;
           char *buffer;
           int i;
    -      while (n < total && tostring(L, top-n-1)) {  /* collect total length */
    -        tl += tsvalue(top-n-1)->tsv.len;
    -        n++;
    +      /* collect total length */
    +      for (n = 1; n < total && tostring(L, top-n-1); n++) {
    +        size_t l = tsvalue(top-n-1)->tsv.len;
    +        if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow");
    +        tl += l;
           }
    -      if (tl > MAX_SIZET) luaG_runerror(L, "string size overflow");
           buffer = luaZ_openspace(L, &G(L)->buff, tl);
           tl = 0;
           for (i=n; i>0; i--) {  /* concat all strings */
    


  2. lua_getupvalue and lua_setupvalue do not check for index too small.
    reported by Mike Pall on 6 Jun 2004. Fixed in 5.1 and 5.0.3.

    Example:

    debug.getupvalue(function() end, 0)
    
    Patch:
    lapi.c
    941c941
    <     if (n > f->c.nupvalues) return NULL;
    ---
    >     if (!(1 <= n && n <= f->c.nupvalues)) return NULL;
    947c947
    <     if (n > p->sizeupvalues) return NULL;
    ---
    >     if (!(1 <= n && n <= p->sizeupvalues)) return NULL;
    


  3. Values held in open upvalues of suspended threads may be incorrectly collected.
    reported by Spencer Schumann on 31 Dec 2004. Fixed in 5.1 and 5.0.3.

    Example:

    local thread_id = 0
    local threads = {}
    
    function fn(thread)
        thread_id = thread_id + 1
        threads[thread_id] = function()
                                 thread = nil
                             end
        coroutine.yield()
    end
    
    while true do
        local thread = coroutine.create(fn)
        coroutine.resume(thread, thread)
    end
    
    Patch:
    lgc.c:
    221,224c221,222
    <       if (!u->marked) {
    <         markobject(st, &u->value);
    <         u->marked = 1;
    <       }
    ---
    >       markobject(st, u->v);
    >       u->marked = 1;
    


  4. rawset and rawget do not ignore extra arguments.
    reported by Romulo Bahiense on 11 Mar 2005. Fixed in 5.1 and 5.0.3.

    Example:

    a = {}
    rawset(a, 1, 2, 3)
    print(a[1], a[2])    -- should be 2 and nil
    

    Patch:

    lbaselib.c:
    175a176
    >   lua_settop(L, 2);
    183a185
    >   lua_settop(L, 3);
    


  5. Weak tables that survive one collection are never collected.
    reported by Chromix on 2 Jan 2006. Fixed in 5.1 and 5.0.3.

    Example:

    a = {}
    print(gcinfo())
    for i = 1, 10000 do
      a[i] = setmetatable({}, {__mode = "v"})
    end
    collectgarbage()
    a = nil
    collectgarbage()
    print(gcinfo())
    

    Patch:

    lgc.c
    @@ -366,7 +366,7 @@
       GCObject *curr;
       int count = 0;  /* number of collected items */
       while ((curr = *p) != NULL) {
    -    if (curr->gch.marked > limit) {
    +    if ((curr->gch.marked & ~(KEYWEAK | VALUEWEAK)) > limit) {
           unmark(curr);
           p = &curr->gch.next;
         }
    


  6. Garbage collector does not compensate enough for finalizers.

  7. Some "not not" expressions may not result in boolean values.
    reported by Aaron Brown on 30 Jun 2005. Fixed in 5.0.3.

    Example:

    -- should print false, but prints nil
    print(not not (nil and 4))
    


  8. On some machines, closing a "piped file" (created with io.popen) may crash Lua.
    reported by Mike Pall on 23 May 2005. Fixed in 5.0.3.

    Example:

      f = io.popen("ls")
      f:close()
    


Lua 5.0 1 ·  2 ·  3 ·  4 ·  5 ·  6 ·  7 ·  8 ·  9 ·  10 ·  11 ·  12 ·  13 ·  14 ·  15 ·  16 ·  17

  1. lua_closethread exists only in the manual.
    reported by Nguyen Binh on 28 Apr 2003. Fixed in 5.0.2.

    Patch: The manual is wrong: threads are subject to garbage collection.


  2. Attempt to resume a running coroutine crashes Lua.
    reported by Alex Bilyk on 9 May 2003. Fixed in 5.0.2.

    Example:

    function co_func (current_co)
       coroutine.resume(co)
    end
    co = coroutine.create(co_func)
    coroutine.resume(co)
    coroutine.resume(co)     --> seg. fault
    
    Patch:
    ldo.c:
    325,326c325
    <     if (nargs >= L->top - L->base)
    <       luaG_runerror(L, "cannot resume dead coroutine");
    ---
    >     lua_assert(nargs < L->top - L->base);
    329c328,329
    <   else if (ci->state & CI_YIELD) {  /* inside a yield? */
    ---
    >   else {  /* inside a yield */
    >     lua_assert(ci->state & CI_YIELD);
    344,345d343
    <   else
    <     luaG_runerror(L, "cannot resume non-suspended coroutine");
    351a350,358
    > static int resume_error (lua_State *L, const char *msg) {
    >   L->top = L->ci->base;
    >   setsvalue2s(L->top, luaS_new(L, msg));
    >   incr_top(L);
    >   lua_unlock(L);
    >   return LUA_ERRRUN;
    > }
    >
    >
    355a363,368
    >   if (L->ci == L->base_ci) {
    >     if (nargs >= L->top - L->base)
    >       return resume_error(L, "cannot resume dead coroutine");
    >   }
    >   else if (!(L->ci->state & CI_YIELD))  /* not inside a yield? */
    >     return resume_error(L, "cannot resume non-suspended coroutine");
    


  3. file:close cannot be called without a file. (results in seg. fault.)
    reported by Tuomo Valkonen on 27 May 2003. Fixed in 5.0.2.

    Example:

    > io.stdin.close()    -- correct call should be io.stdin:close()
    
    Patch:
    liolib.c:
    161c161
    <   if (lua_isnone(L, 1)) {
    ---
    >   if (lua_isnone(L, 1) && lua_type(L, lua_upvalueindex(1)) == LUA_TTABLE) {
    


  4. C functions may have stacks larger than current top.
    reported by Alex Bilyk on 9 Jun 2003. Fixed in 5.0.2.

    Example:

    Must recompile Lua with a change in lua.c and with lua_assert defined:
    lua.c:
    381a382
    >   lua_checkstack(l, 1000);
    
    Patch:
    lgc.c:
    247c247
    <     if (!(ci->state & CI_C) && lim < ci->top)
    ---
    >     if (lim < ci->top)
    


  5. 'pc' address is invalidated when a coroutine is suspended.
    reported by Nick Trout on 7 Jul 2003. Fixed in 5.0.2.

    Example:

    function g(x)
        coroutine.yield(x)
    end
    
    function f (i)
      debug.sethook(print, "l")
      for j=1,1000 do
        g(i+j)
      end
    end
    
    co = coroutine.wrap(f)
    co(10)
    pcall(co)
    pcall(co)
    
    Patch:
    lvm.c:
    402d401
    <   L->ci->u.l.pc = &pc;
    405a405
    >   L->ci->u.l.pc = &pc;
    676,678c676
    <           lua_assert(ci->u.l.pc == &pc &&
    <                      ttisfunction(ci->base - 1) &&
    <                      (ci->state & CI_SAVEDPC));
    ---
    >           lua_assert(ttisfunction(ci->base - 1) && (ci->state & CI_SAVEDPC));
    


  6. Userdata to be collected still counts into new GC threshold, increasing memory consumption.
    reported by Roberto on 25 Jul 2003. Fixed in 5.0.2.

    Example:

    a = newproxy(true)
    getmetatable(a).__gc = function () end
    for i=1,10000000 do
      newproxy(a)
      if math.mod(i, 10000) == 0 then print(gcinfo()) end
    end
    
    Patch:
    lgc.h:
    18c18
    < void luaC_separateudata (lua_State *L);
    ---
    > size_t luaC_separateudata (lua_State *L);
    
    lgc.c:
    113c113,114
    < void luaC_separateudata (lua_State *L) {
    ---
    > size_t luaC_separateudata (lua_State *L) {
    >   size_t deadmem = 0;
    127a129
    >       deadmem += sizeudata(gcotou(curr)->uv.len);
    136a139
    >   return deadmem;
    390c393
    < static void checkSizes (lua_State *L) {
    ---
    > static void checkSizes (lua_State *L, size_t deadmem) {
    400c403
    <   G(L)->GCthreshold = 2*G(L)->nblocks;  /* new threshold */
    ---
    >   G(L)->GCthreshold = 2*G(L)->nblocks - deadmem;  /* new threshold */
    454c457,458
    < static void mark (lua_State *L) {
    ---
    > static size_t mark (lua_State *L) {
    >   size_t deadmem;
    467c471
    <   luaC_separateudata(L);  /* separate userdata to be preserved */
    ---
    >   deadmem = luaC_separateudata(L);  /* separate userdata to be preserved */
    475a480
    >   return deadmem;
    480c485
    <   mark(L);
    ---
    >   size_t deadmem = mark(L);
    482c487
    <   checkSizes(L);
    ---
    >   checkSizes(L, deadmem);
    


  7. IBM AS400 (OS400) has sizeof(void *)==16, and a '%p' may generate up to 60 characters in a 'printf', causing a buffer overflow in tostring.
    reported by David Burgess on 25 Aug 2003. Fixed in 5.0.2.

    Example:

    print{}  -- on an AS400 machine
    Patch:
    liolib.c:
    178c178
    <   char buff[32];
    ---
    >   char buff[128];
    
    lbaselib.c:
    327c327
    <   char buff[64];
    ---
    >   char buff[128];
    


  8. Syntax local function does not increment stack size.
    reported by Rici Lake on 26 Sep 2003. Fixed in 5.0.2.

    Example:

    -- must run this with precompiled code
    local a,b,c
    local function d () end
    
    Patch:
    lparser.c:
    1143a1144
    >   FuncState *fs = ls->fs;
    1145c1146,1147
    <   init_exp(&v, VLOCAL, ls->fs->freereg++);
    ---
    >   init_exp(&v, VLOCAL, fs->freereg);
    >   luaK_reserveregs(fs, 1);
    1148c1150,1152
    <   luaK_storevar(ls->fs, &v, &b);
    ---
    >   luaK_storevar(fs, &v, &b);
    >   /* debug information will only see the variable after this point! */
    >   getlocvar(fs, fs->nactvar - 1).startpc = fs->pc;
    


  9. Count hook may be called without being set.
    reported by Andreas Stenius on 6 Oct 2003. Fixed in 5.0.2.

    Example:
    Set your hooks as below. It is weird to use a positive count without setting the count hook, but it is not wrong.

    lua_sethook(L, my_hook, LUA_MASKLINE | LUA_MASKRET, 1);
    
    Patch:
    lvm.c:
    69c69
    <   if (mask > LUA_MASKLINE) {  /* instruction-hook set? */
    ---
    >   if (mask & LUA_MASKCOUNT) {  /* instruction-hook set? */
    


  10. dofile eats one return value when called without arguments.
    reported by Frederico Abraham on 15 Jan 2004. Fixed in 5.0.2.

    Example:

    a,b = dofile()   --< here you enter `return 1,2,3 '
    print(a,b)   --> 2   3   (should be 1 and 2)
    
    Patch:
    lbaselib.c:
    313a314
    >   int n = lua_gettop(L);
    317c318
    <   return lua_gettop(L) - 1;
    ---
    >   return lua_gettop(L) - n;
    


  11. String concatenation may cause arithmetic overflow, leading to a buffer overflow.

  12. lua_getupvalue and lua_setupvalue do not check for index too small.

  13. rawset and rawget do not ignore extra arguments.

  14. Weak tables that survive one collection are never collected.

  15. Garbage collector does not compensate enough for finalizers.

  16. Some "not not" expressions may not result in boolean values.

  17. On some machines, closing a "piped file" (created with io.popen) may crash Lua.

Lua 4.0

Most bugs in Lua 4.0 have been fixed in Lua 4.0.1.

  1. Parser did not accept a ';' after a 'return'.
    reported by lhf on 29 Nov 2000. Fixed in 4.0.1.

  2. When 'read' fails it must return nil (and not no value).
    reported by Carlos Cassino on 22 Dec 2000. Fixed in 5.0.

  3. lua_pushuserdata(L,NULL) does not work.
    reported by Edgar Toernig on 1 Feb 2001. Fixed in 4.0.1.

  4. «while 1 dostring[[print('hello\n')]] end» never reclaims memory.
    reported by Andrew Paton on 2 Feb 2001. Fixed in 4.0.1.

  5. ESC (which starts precompiled code) in C is \33, not \27.
    reported by Edgar Toernig on 6 Feb 2001. Fixed in 4.0.1.

  6. Error message for '%a' gave wrong line number.
    reported by Leonardo Constantino on 10 Jul 2001. Fixed in 4.0.1.

  7. Segmentation fault when rawget/rawset get extra arguments.
    reported by Eric Mauger on 21 Dec 2001. Fixed in 4.0.1.

  8. Line hook gets wrong 'ar'.
    reported by Daniel Sinclair on 19 Jun 2002. Fixed in 4.0.1.

  9. 'protectedparser' may run GC and then collect 'filename' (in 'parse_file').
    reported by Alex Bilyk on 19 Jun 2002. Fixed in 4.0.1.

  10. ULONG_MAX>>10 may not fit into an int.
    reported by Jeff Petkau on 21 Nov 2002. Fixed in 5.0.


Last update: Sun May 11 00:27:37 BRT 2008