--[=[ ** lua.stx / llex.c Tue Dec 2 10:45:48 EDT 1997 >> BUG: "lastline" was not reset on function entry, so debug information >> started only in the 2nd line of a function. ================================================================= --- Version 3.1 alpha ** lua.c Thu Jan 15 14:34:58 EDT 1998 >> must include "stdlib.h" (for "exit()"). ** lbuiltin.c / lobject.h Thu Jan 15 14:34:58 EDT 1998 >> MAX_WORD may be bigger than MAX_INT (by lhf) ** llex.c Mon Jan 19 18:17:18 EDT 1998 >> wrong line number (+1) in error report when file starts with "#..." ** lstrlib.c Tue Jan 27 15:27:49 EDT 1998 >> formats like "%020d" were considered too big (3 digits); moreover, >> some sistems limit printf to at most 500 chars, so we can limit sizes >> to 2 digits (99). ** lapi.c Tue Jan 27 17:12:36 EDT 1998 >> "lua_getstring" may create a new string, so should check GC ** lstring.c / ltable.c Wed Jan 28 14:48:12 EDT 1998 >> tables can become full of "empty" slots, and keep growing without limits. ** lstrlib.c Mon Mar 9 15:26:09 EST 1998 >> gsub('a', '(b?)%1*' ...) loops (because the capture is empty). ** lstrlib.c Mon May 18 19:20:00 EST 1998 >> arguments for "format" 'x', 'X', 'o' and 'u' must be unsigned int. ================================================================= --- Version 3.1 ** liolib.c / lauxlib.c Mon Sep 7 15:57:02 EST 1998 >> function "luaL_argerror" prints wrong argument number (from a user's point of view) when functions have upvalues. ** lstrlib.c Tue Nov 10 17:29:36 EDT 1998 >> gsub/strfind do not check whether captures are properly finished. (by roberto/tomas) ** lbuiltin.c Fri Dec 18 11:22:55 EDT 1998 >> "tonumber" goes crazy with negative numbers in other bases (not 10), because "strtol" returns long, not unsigned long. (by Visual C++) ** lstrlib.c Mon Jan 4 10:41:40 EDT 1999 >> "format" does not check size of format item (such as "%00000...00000d"). ** lapi.c Wed Feb 3 14:40:21 EDT 1999 >> getlocal cannot return the local itself, since lua_isstring and lua_isnumber can modify it. ** lstrlib.c Thu Feb 4 17:08:50 EDT 1999 >> format "%s" may break limit of "sprintf" on some machines. (by Marcelo Sales) ** lzio.c Thu Mar 4 11:49:37 EST 1999 >> file stream cannot call fread after EOF. (by lhf) ================================================================= --- Version 3.2 (beta) ** lstrlib.c Fri Apr 30 11:10:20 EST 1999 >> '$' at end of pattern was matching regular '$', too. (by anna; since 2.5) ** lbuiltin.c Fri May 21 17:15:11 EST 1999 >> foreach, foreachi, foreachvar points to function in stack when stack can be reallocated. (by tomas; since 3.2 beta) ** lparser.c Wed Jun 16 10:32:46 EST 1999 >> cannot assign to unlimited variables, because it causes overflow in the number of returns of a function. (since 3.1) ================================================================= --- Version 3.2 ** lmathlib.c Wed Aug 18 11:28:38 EST 1999 >> random(0) and random(x,0) are wrong (0 is read as no argument!). (by Dave Bollinger; since 3.1) ** lparser.c Thu Sep 2 10:07:20 EST 1999 >> in the (old) expression << ls->fs->f->consts[checkname(ls)] >>, checkname could realloc f->consts. (by Supratik Champati; since 3.2 beta) ** lobject.c / lbuiltin.c Wed Sep 8 17:41:54 EST 1999 >> tonumber'e1' and tonumber(' ', x), for x!=10, gave 0 instead of nil. (since 3.1) ** lstrlib.c Thu Nov 11 14:36:30 EDT 1999 >> `strfind' does not handle \0 in plain search. (by Jon Kleiser; since 3.1) ** lparser.c Wed Dec 29 16:05:43 EDT 1999 >> return gives wrong line in debug information (by lhf; since 3.2 [at least]) ** ldo.c Thu Dec 30 16:39:33 EDT 1999 >> cannot reopen stdin (for binary mode) (by lhf & roberto; since 3.1) ** lapi.c Thu Mar 2 09:41:53 EST 2000 >> lua_settable should check stack space (it could call a T.M.) (by lhf & celes; since 3.2; it was already fixed by fixed stack) ** lparser.c Mon Apr 3 09:59:06 EST 2000 >> '%' should be in expfollow (by Edgar Toernig; since 3.1; it was already fixed) ** lbuiltin.c Mon Apr 3 10:05:05 EST 2000 >> tostring() without arguments gives seg. fault. (by Edgar Toernig; since 3.0) ================================================================= --- Version 4.0 alpha Tested with full test suites (as locked in Mon Apr 24 14:23:11 EST 2000) in the following platforms: * Linux - gcc, g++ * AIX - gcc * Solaris - gcc, cc * IRIX - cc, cc-purify * Windows - Visual C++ (.c e .cpp, warning level=4) ** lstrlib.c Tue May 2 15:27:58 EST 2000 >> `strfind' gets wrong subject length when there is an offset (by Jon Kleiser; since 4.0a) ** lparser.c Fri May 12 15:11:12 EST 2000 >> first element in a list constructor is not adjusted to one value >> (e.g. «a = {gsub('a','a','')}») (by Tomas; since 4.0a) ** lparser.c Wed May 24 14:50:16 EST 2000 >> record-constructor starting with an upvalue name gets an error >> (e.g. «local a; function f() x = {a=1} end») (by Edgar Toernig; since 3.1) ** lparser.c Tue Aug 29 15:56:05 EST 2000 >> error message for `for' uses `while' (since 4.0a; already corrected) ** lgc.c Tue Aug 29 15:57:41 EST 2000 >> gc tag method for nil could call line hook (by ry; since ?) ================================================================= --- Version 4.0 Beta ** liolib.c Fri Sep 22 15:12:37 EST 2000 >> `read("*w")' should return nil at EOF (by roberto; since 4.0b) ** lvm.c Mon Sep 25 11:47:48 EST 2000 >> lua_gettable does not get key from stack top (by Philip Yi; since 4.0b) ** lgc.c Mon Sep 25 11:50:48 EST 2000 >> GC may crash when checking locked C closures (by Philip Yi; since 4.0b) ** lapi.c Wed Sep 27 09:50:19 EST 2000 >> lua_tag should return LUA_NOTAG for non-valid indices (by Paul Hankin; since 4.0b) ** llex.h / llex.c / lparser.c Wed Sep 27 13:39:45 EST 2000 >> parser overwrites semantic information when looking ahead >> (e.g. «a = {print'foo'}») (by Edgar Toernig; since 4.0b, deriving from previous bug) ** liolib.c Thu Oct 26 10:50:46 EDT 2000 >> in function `read_file', realloc() doesn't free the buffer if it can't >> allocate new memory (by Mauro Vezzosi; since 4.0b) ================================================================= --- Version 4.0 ** lparser.c Wed Nov 29 09:51:44 EDT 2000 >> parser does not accept a `;' after a `return' (by lhf; since 4.0b) ** liolib.c Fri Dec 22 15:30:42 EDT 2000 >> when `read' fails it must return nil (and not no value) (by cassino; since at least 3.1) ** lstring.c/lapi.c Thu Feb 1 11:55:45 EDT 2001 >> lua_pushuserdata(L, NULL) is buggy (by Edgar Toernig; since 4.0) ** ldo.c Fri Feb 2 14:06:40 EDT 2001 >> «while 1 dostring[[print('hello\n')]] end» never reclaims memory (by Andrew Paton; since 4.0b) ** lbaselib.c Tue Feb 6 11:57:13 EDT 2001 >> ESC (which starts precompiled code) in C is \33, not \27 (by Edgar Toernig and lhf; since 4.0b) ** lparser.c Tue Jul 10 16:59:18 EST 2001 >> error message for `%a' gave wrong line number (by Leonardo Constantino; since 4.0) ** lbaselib.c Fri Dec 21 15:21:05 EDT 2001 >> seg. fault when rawget/rawset get extra arguments (by Eric Mauger; since 4.0b) ** lvm.c Wed Jun 19 13:28:20 EST 2002 >> line hook gets wrong `ar' (by Daniel C. Sinclair; since 4.0.b) ** ldo.c Wed Jun 19 13:31:49 EST 2002 >> `protectedparser' may run GC, and then collect `filename' >> (in function `parse_file') (by Alex Bilyk; since 4.0) ================================================================= --- Version 5.0 alpha ** lgc.c Fri Aug 30 13:49:14 EST 2002 >> GC metamethod stored in a weak metatable being collected together with >> userdata may not be cleared properly (by Roberto; since 5.0a) ** lapi.c Thu Nov 21 11:00:00 EST 2002 >> ULONG_MAX>>10 may not fit into an int (by Jeff Petkau; since 4.0) ** lparser.c Fri Dec 6 17:06:40 UTC 2002 >> scope of generic for variables is not sound (by Gavin Wraith; since 5.0a) ================================================================= --- Version 5.0 beta ** lbaselib.c Fri Dec 20 09:53:19 UTC 2002 >> `resume' was checking the wrong value for stack overflow (by Maik Zimmermann; since 5.0b) ** ldo.c Thu Jan 23 11:29:06 UTC 2003 >> error during garbage collection in luaD_protectedparser is not being >> protected (by Benoit Germain; since 5.0a) ** ldo.c (and others) Fri Feb 28 14:20:33 EST 2003 >> GC metamethod calls could mess C/Lua stack syncronization (by Roberto; since 5.0b) ** lzio.h/zlio.c Thu Mar 20 11:40:12 EST 2003 >> zio mixes a 255 as first char in a buffer with EOZ (by lhf; since 5.0a) --]=] ----------------------------------------------------------------- -- Lua 5.0 (final) Bug{ what = [[lua_closethread exists only in the manual]], report = [[by Nguyen Binh, 28/04/2003]], patch = [[no patch; the manual is wrong]], } Bug{ what = [[attempt to resume a running coroutine crashes Lua]], example = [[ function co_func (current_co) coroutine.resume(co) end co = coroutine.create(co_func) coroutine.resume(co) coroutine.resume(co) --> seg. fault ]], report = [[by Alex Bilyk, 09/05/2003]], 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"); ]], } Bug{ what = [[file:close cannot be called without a file. (results in seg fault)]], example = [[ > io.stdin.close() -- correct call shold be io.stdin:close() ]], report = [[by Tuomo Valkonen, 27/05/2003]], patch = [[ * liolib.c: 161c161 < if (lua_isnone(L, 1)) { --- > if (lua_isnone(L, 1) && lua_type(L, lua_upvalueindex(1)) == LUA_TTABLE) { ]], --}} } Bug{ what = [[C functions also may have stacks larger than current top]], example = [[ Must recompile lua with a change in lua.c and with lua_assert defined: * lua.c: 381a382 > lua_checkstack(l, 1000); ]], report = [[Alex Bilyk, 09/06/2003]], patch = [[ * lgc.c: 247c247 < if (!(ci->state & CI_C) && lim < ci->top) --- > if (lim < ci->top) ]], } Bug{ what = [[`pc' address is invalidated when a coroutine is suspended]], 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) ]], report = [[Nick Trout, 07/07/2003]], patch = [[ * lvm.c: 402,403c402,403 < L->ci->u.l.pc = &pc; < if (L->hookmask & LUA_MASKCALL) --- > if (L->hookmask & LUA_MASKCALL) { > L->ci->u.l.pc = &pc; 404a405 > } 405a407 > L->ci->u.l.pc = &pc; 676,678c678 < 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)); ]] } Bug{ what = [[userdata to be collected still counts into new GC threshold, increasing memory consumption]], report = [[Roberto, 25/07/2003]], 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); ]] } Bug{ what=[[IBM AS400 (OS400) has sizeof(void *)==16, and a `%p' may generate up to 60 characters in a `printf'. That causes a buffer overflow in `tostring'.]], report = [[David Burgess, 25/08/2003]], example = [[print{}; (in an AS400 machine)]], patch = [[ * liolib.c: 178c178 < char buff[32]; --- > char buff[128]; * lbaselib.c: 327c327 < char buff[64]; --- > char buff[128]; ]] } Bug{ what = [[syntax `local function' does not increment stack size]], report = [[Rici Lake, 26/09/2003]], 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; ]], } Bug{ what = [[count hook may be called without being set]], report = [[Andreas Stenius, 06/10/2003]], example = [[ set your hooks with lua_sethook(L, my_hook, LUA_MASKLINE | LUA_MASKRET, 1); (It is weird to use a count > 0 without setting the count hook, but it is not wrong.) ]], patch = [[ * lvm.c: 69c69 < if (mask > LUA_MASKLINE) { /* instruction-hook set? */ --- > if (mask & LUA_MASKCOUNT) { /* instruction-hook set? */ ]], } Bug{ what = [[`dofile' eats one return value when called without arguments]], report = [[Frederico Abraham, 15/01/2004]], 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; ]], } ----------------------------------------------------------------- -- Lua 5.0.2 Bug{ what = [[string concatenation may cause arithmetic overflow, leading to a buffer overflow]], report = [[Rici Lake, 20/05/2004]], 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 */ ]] } Bug{ what = [[lua_getupvalue and setupvalue do not check for index too small]], report = [[Mike Pall, ?/2004]], 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; ]] } Bug{ what = [[values holded in open upvalues of suspended threads may be incorrectly collected]], report = [[Spencer Schumann, 31/12/2004]], 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; ]], } Bug{ what = [[rawset/rawget do not ignore extra arguments]], report = [[Romulo Bahiense, 11/03/2005]], 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); ]], } Bug{ what = [[weak tables that survive one collection are never collected]], report = [[Chromix, 02/01/2006]], 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; } ]], } Bug{ what = [[Some "not not exp" may not result in boolean values]], report = [[]], since = [[4.0]], example = [[ -- should print false, but prints nil print(not not (nil and 4)) ]], patch = [[]], } Bug{ what = [[On some machines, closing a "piped file" (created with io.popen) may crash Lua]], report = [[]], since = [[5.0]], example = [[ -- only on some machines f = io.popen("ls") f:close() ]], patch = [[]], } ----------------------------------------------------------------- -- Lua 5.1 Bug{ what = [[In 16-bit machines, expressions and/or with numeric constants as the right operand may result in weird values]], report = [[Andreas Stenius/Kein-Hong Man, 15/03/2006]], 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; } ]], } Bug{ what = [[luaL_checkudata may produce wrong error message]], report = [[Greg Falcon, 21/03/2006]], 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 */ } ]] } Bug{ what = [[ In Windows, when Lua is used in an application that also uses DirectX, it may present an erractic behavior. THIS IS NOT A LUA BUG! 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 ]], } Bug{ what = [[option '%q' in string.format does not handle '\r' correctly.]], 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; ]], } Bug{ what = [[lua_dostring/lua_dofile should return any values returned by the chunk]], 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))) ]], } Bug{ what = [[garbage collector does not compensate enough for finalizers]], 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; ]] } Bug{ what = [[debug hooks may get wrong when mixed with coroutines]], report = [[by Ivko Stanilov, 03/06/2006]], example = [[ co = coroutine.create(function (a,b) coroutine.yield(a, b) return b, "end" end) debug.sethook(co, function() end, "lcr") coroutine.resume(co, 100, 2000) coroutine.resume(co, 100, 2000) ]], 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)); } ]], } ----------------------------------------------------------------- -- Lua 5.1.1 Bug{ what = [[list constructors have wrong limit]], report = [[by Norman Ramsey, June 2006]], since = "Lua 5.1", 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++; } ]], } Bug{ what = [[wrong message error in some cases involving closures]], report = [[Shmuel Zeigerman, on 07/2006]], since = "Lua 5.1", example = [[ local Var local function main() NoSuchName (function() Var=0 end) end main() --> lua5.1: temp:3: attempt to call upvalue 'Var' (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: { ]], } Bug{ what = [[string.format("%") may read past the string]], report = [[Roberto, on 09/2006]], since = [[5.0 (at least)]], 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 */ ]], } Bug{ what = [[os.date throws an error when result is the empty string]], report = [[]], since = [[4.0]], 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); } ]], } Bug{ what = [[setfenv accepts invalid 1st argument]], report = [[Doug Rogers, on 02/2007]], since = [[5.0]], 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); ]], } Bug{ what = [[wrong code for arithmetic expressions in some specific scenarios]], report = [[Thierry Grellier, on 01/2007]], since = [[5.1]], 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; + } } } ]], } Bug{ what = [[assignment of nil to parameter may be optimized away]], report = [[Thomas Lauer, on 03/2007]], since = [[5.1]], 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; + } } } } ]], } Bug{ what = [[__concat metamethod converts numbers to strings]], report = [[Paul Winwood, on 12/2006]], since = [[5.0]], 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; ]], } Bug{ what = [[As a library, loadlib.c should not access Lua internals (via lobject.h)]], report = [[Jérôme Vuarand, on 03/2007]], since = [[5.0 (at least)]], example = [[the bug has no effect on external behavior]], patch = [[remove the '#include "lobject.h" and use 'lua_pushfstring' instead of 'luaO_pushfstring']], } ----------------------------------------------------------------- -- Lua 5.1.2 Bug{ what = [[Lua may close standard files, which then may be used by C]], report = [[David Manura/Ross Berteig, on 04/2007]], since = [[ ]], example = [[ io.close(io.stderr) -- in some systems, following attempts to write to 'stderr' may crash a = a + 1 ]], patch = [[ ]], } Bug{ what = [[code generated for "-nil", "-true", and "-false" is wrong]], report = [[David Manura/Rici Lake, on 04/2007]], since = [[5.1]], 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; ]], } Bug{ what = [[Count hook may be called without being set.]], report = [[Mike Pall, on 05/2007]], since = [[?]], example = [[ ]], 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; ]], } Bug{ what = [[recursive coroutines may overflow C stack]], report = [[ , on ]], since = [[5.0]], 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'.) ]], } Bug{ what = [[wrong error message in some concatenations]], report = [[Alex Davies, on 05/2007]], since = [[5.1.2]], example = [[a = nil; a = (1)..a]], patch = [[ ldebug.c: @@ -563,8 +563,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"); } ]], } Bug{ what = [[Very small numbers all collide in the hash function. (This creates only performance problems; the behavoir is correct.)]], report = [[, on ]], since = [[Lua 5.0]], example = [[ ]], 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); ]], } Bug{ what = [[Too many variables in an assignment may cause a C stack overflow]], report = [[Mike Pall, on 07/2007]], since = [[5.0]], 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 */ ]], } Bug{ what = [[An error in a module loaded through the '-l' option shows no traceback]], report = [[David Manura, on 08/2007]], since = [[5.1]], 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)); } ]], } Bug{ what = [['gsub' may go wild when wrongly called without its third argument and with a large subject]], report = [[Florian Berger, on 10/2007]], since = [[5.1]], 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); ]], } Bug{ what = [[table.remove removes last element of a table when given an out-of-bound index]], report = [[Patrick Donnelly, on 11/2007]], since = [[at least 5.0]], 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 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; ]], } Bug{ what = [[stand-alone interpreter shows incorrect error message when the "message" is a coroutine]], report = [[Patrick Donnelly, on 17/12/2007]], since = [[i ]], 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); ]], } Bug{ what = [[debug.sethook/gethook may overflow the thread's stack]], report = [[Ivko Stanilov, on 2008/01/04]], since = [[5.1]], 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.3 Bug{ what = [[LUAI_MAXCSTACK must be smaller than -LUA_REGISTRYINDEX]], report = [[Patrick Donnelly, on 2008/02/11]], since = [[5.1.3]], 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 ]], } Bug{ what = [[coroutine.resume pushes element without ensuring stack size]], report = [[on 2008/02/11]], since = [[5.0]], 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; ]], } Bug{ what = [[lua_checkstack may have arithmetic overflow for large 'size']], report = [[Patrick Donnelly, on 2008/02/12]], since = [[5.0]], example = [[ print(unpack({1,2,3}, 0, 2^31-3)) ]], patch = [[ --- lapi.c 2008/01/03 15:20:39 2.55.1.3 +++ lapi.c 2008/02/14 16:05:21 @@ -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; ]], } Bug{ what = [[unpack with maximum indices may crash due to arithmetic overflow]], report = [[Patrick Donnelly, on 2008/02/12]], since = [[5.1]], example = [[ print(unpack({1,2,3}, 2^31-1, 2^31-1)) ]], patch = [[ --- lbaselib.c 2008/02/11 16:24:24 1.191.1.5 +++ lbaselib.c 2008/02/14 16:10:25 @@ -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; } ]], } Bug{ what = [[The validator for precompiled code has several flaws that allow malicious binary code to crash the application]], report = [[Peter Cawley, on 2008/03/24]], since = [[5.0]], 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 2007/12/28 15:32:23 2.29.1.3 +++ ldebug.c 2008/04/04 15:15:40 @@ -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: { ]], } Bug{ what = [[maliciously crafted precompiled code can blow the C stack]], report = [[Greg Falcon, on 2008/03/25]], since = [[5.0]], 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 2008/04/04 16:00:45 2.7.1.3 +++ lundump.c 2008/04/04 19:51:41 2.7.1.4 @@ -1,5 +1,5 @@ /* -** $Id: bugs,v 1.110 2011/08/17 20:38:51 roberto Exp roberto $ +** $Id: bugs,v 1.110 2011/08/17 20:38:51 roberto Exp roberto $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ @@ -161,7 +161,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 +177,7 @@ LoadDebug(S,f); IF (!luaG_checkcode(f), "bad code"); S->L->top--; + S->L->nCcalls--; return f; } ]], } Bug{ what = [[code validator may reject (maliciously crafted) correct code]], report = [[Greg Falcon, on 2008/03/26]], since = [[5.0]], 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 2008/04/04 15:30:05 2.29.1.4 +++ ldebug.c 2008/04/04 15:47:10 @@ -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; ]], } Bug{ what = [[maliciously crafted precompiled code can inject invalid boolean values into Lua code]], report = [[Greg Falcon, on 2008/03/27]], since = [[5.0]], 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 2008/01/18 16:39:11 2.7.1.2 +++ lundump.c 2008/04/04 15:50:39 @@ -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)); ]], } Bug{ what = [['string.byte' gets confused with some out-of-range negative indices]], report = [[Mike Pall, on 2008/06/03]], since = [[5.1]], example = [[ print(string.byte("abc", -5)) --> 97 98 99 (should print nothing) ]], patch = [[ --- lstrlib.c 2007/12/28 15:32:23 1.132.1.3 +++ lstrlib.c 2008/07/05 11:53:42 @@ -35,7 +35,8 @@ static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) { /* relative string position: negative means back from end */ - return (pos>=0) ? pos : (ptrdiff_t)len+pos+1; + if (pos < 0) pos += (ptrdiff_t)len + 1; + return (pos >= 0) ? pos : 0; } ]], } Bug{ what = [[user-requested GC step may loop forever]], report = [[Makoto Hamanaka, on 2008/07/01]], since = [[5.1]], example = [[ collectgarbage("setpause", 100) -- small value collectgarbage("setstepmul", 2000) -- large value collectgarbage("step",0) ]], patch = [[ --- lapi.c 2008/02/14 16:46:39 2.55.1.4 +++ lapi.c 2008/07/04 18:34:48 @@ -929,10 +929,13 @@ g->GCthreshold = g->totalbytes - a; else g->GCthreshold = 0; - while (g->GCthreshold <= g->totalbytes) + while (g->GCthreshold <= g->totalbytes) { luaC_step(L); - if (g->gcstate == GCSpause) /* end of cycle? */ - res = 1; /* signal it */ + if (g->gcstate == GCSpause) { /* end of cycle? */ + res = 1; /* signal it */ + break; + } + } break; } case LUA_GCSETPAUSE: { ]], } Bug{ what = [['module' may change the environment of a C function]], report = [[Peter Cawley, on 2008/07/16]], since = [[5.1]], example = [[ pcall(module, "xuxu") assert(debug.getfenv(pcall) == xuxu) ]], patch = [[ --- loadlib.c 2007/12/28 14:58:43 1.52.1.2 +++ loadlib.c 2008/08/05 19:39:00 @@ -506,8 +506,11 @@ static void setfenv (lua_State *L) { lua_Debug ar; - lua_getstack(L, 1, &ar); - lua_getinfo(L, "f", &ar); + if (lua_getstack(L, 1, &ar) == 0 || + lua_getinfo(L, "f", &ar) == 0 || /* get calling function */ + lua_iscfunction(L, -1)) + luaL_error(L, "function " LUA_QL("module") + " not called from a Lua function"); lua_pushvalue(L, -2); lua_setfenv(L, -2); lua_pop(L, 1); ]], } Bug{ what = [[internal macro 'svalue' is wrong]], report = [[Martijn van Buul, on 2008/08/04]], since = [[5.1]], example = [[ /* in luaconf.h */ #define LUAI_USER_ALIGNMENT_T union { char b[32]; } ]], patch = [[ --- lobject.h 2007/12/27 13:02:25 2.20.1.1 +++ lobject.h 2008/08/05 19:40:48 @@ -210,3 +210,3 @@ #define getstr(ts) cast(const char *, (ts) + 1) -#define svalue(o) getstr(tsvalue(o)) +#define svalue(o) getstr(rawtsvalue(o)) ]], } ----------------------------------------------------------------- -- Lua 5.1.4 Bug{ what = [[malicious zero-length string in binary code may segfault Lua]], report = [[Peter Cawley, on 2008/09/01]], since = [[5.1]], example = [[ loadstring(('').dump(function()X''end):gsub('\2%z%z%zX','\0\0\0'))() ]], patch = [[ ]], } Bug{ what = [[wrong code generation for some particular boolean expressions]], report = [[Brian Kelley, on 2009/04/15]], since = [[5.0]], example = [[ print(((1 or false) and true) or false) --> 1 -- should be 'true' ]], patch = [[ --- lcode.c 2007/12/28 15:32:23 2.25.1.3 +++ lcode.c 2009/06/15 14:07:34 @@ -544,15 +544,18 @@ pc = NO_JUMP; /* always true; do nothing */ break; } - case VFALSE: { - pc = luaK_jump(fs); /* always jump */ - break; - } case VJMP: { invertjump(fs, e); pc = e->u.s.info; break; } + case VFALSE: { + if (!hasjumps(e)) { + pc = luaK_jump(fs); /* always jump */ + break; + } + /* else go through */ + } default: { pc = jumponcond(fs, e, 0); break; @@ -572,14 +575,17 @@ pc = NO_JUMP; /* always false; do nothing */ break; } - case VTRUE: { - pc = luaK_jump(fs); /* always jump */ - break; - } case VJMP: { pc = e->u.s.info; break; } + case VTRUE: { + if (!hasjumps(e)) { + pc = luaK_jump(fs); /* always jump */ + break; + } + /* else go through */ + } default: { pc = jumponcond(fs, e, 1); break; ]], } Bug{ what = [['luaV_settable' may invalidate a reference to a table and try to reuse it]], report = [[Mark Feldman, on 2009/06/27]], since = [[5.0]], example = [[ grandparent = {} grandparent.__newindex = function(s,_,_) print(s) end parent = {} parent.__newindex = parent setmetatable(parent, grandparent) child = setmetatable({}, parent) child.foo = 10 --> (crash on some machines) ]], patch = [[ --- lvm.c 2007/12/28 15:32:23 2.63.1.3 +++ lvm.c 2009/07/01 20:36:59 @@ -133,6 +133,7 @@ void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { int loop; + TValue temp; for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue *tm; if (ttistable(t)) { /* `t' is a table? */ @@ -152,7 +153,9 @@ callTM(L, tm, t, key, val); return; } - t = tm; /* else repeat with `tm' */ + /* else repeat with `tm' */ + setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */ + t = &temp; } luaG_runerror(L, "loop in settable"); } ]], } Bug{ what = [[smart use of varargs may create functions that return too many arguments and overflow the stack of C functions]], report = [[Patrick Donnelly, on 2008/12/10]], since = [[]], example = [[ local function lunpack(i, ...) if i == 0 then return ... else return lunpack(i-1, 1, ...) end end Now, if C calls lunpack(n) with a huge n, it may end with too many values in its stack and confuse its stack indices. ]], patch = [[ ]], } Bug{ what = [['debug.getfenv' does not check whether it has an argument]], report = [[Patrick Donnelly, 2009/07/30]], since = [[5.1]], example = [[debug.getfenv() -- should raise an error]], patch = [[ --- ldblib.c 2008/01/21 13:11:21 1.104.1.3 +++ ldblib.c 2009/08/04 18:43:12 @@ -45,6 +45,7 @@ static int db_getfenv (lua_State *L) { + luaL_checkany(L, 1); lua_getfenv(L, 1); return 1; } ]], } Bug{ what = [[GC may get stuck during a parser and avoids proper resizing of the string table, making its lists grow too much and degrading performance]], report = [[Sean Conner, 2009/11/10]], since = [[5.1]], example = [[See http://lua-users.org/lists/lua-l/2009-11/msg00463.html]], patch = [[ --- llex.c 2007/12/27 13:02:25 2.20.1.1 +++ llex.c 2009/11/23 14:49:40 @@ -118,8 +118,10 @@ lua_State *L = ls->L; TString *ts = luaS_newlstr(L, str, l); TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */ - if (ttisnil(o)) + if (ttisnil(o)) { setbvalue(o, 1); /* make sure `str' will not be collected */ + luaC_checkGC(L); + } return ts; } ]] } Bug{ what = [['string.format' may get buffer as an argument when there are missing arguments and format string is too long]], report = [[Roberto I., 2010/04/12]], since = [[5.0]], example = [[ x = string.rep("x", 10000) .. "%d" print(string.format(x)) -- gives wrong error message ]], patch = [[ --- lstrlib.c 2008/07/11 17:27:21 1.132.1.4 +++ lstrlib.c 2010/05/14 15:12:53 @@ -754,6 +754,7 @@ static int str_format (lua_State *L) { + int top = lua_gettop(L); int arg = 1; size_t sfl; const char *strfrmt = luaL_checklstring(L, arg, &sfl); @@ -768,7 +769,8 @@ else { /* format item */ char form[MAX_FORMAT]; /* to store the format (`%...') */ char buff[MAX_ITEM]; /* to store the formatted item */ - arg++; + if (++arg > top) + luaL_argerror(L, arg, "no value"); strfrmt = scanformat(L, strfrmt, form); switch (*strfrmt++) { case 'c': { ]] } Bug{ what = [['io.read(op, "*n")' may return garbage if second read fails]], report = [[Roberto I., 2010/04/12]], since = [[5.0]], example = [[ print(io.read("*n", "*n")) --<< enter "10 hi" --> file (0x884420) nil ]], patch = [[ --- liolib.c 2008/01/18 17:47:43 2.73.1.3 +++ liolib.c 2010/05/14 15:29:29 @@ -276,7 +276,10 @@ lua_pushnumber(L, d); return 1; } - else return 0; /* read fails */ + else { + lua_pushnil(L); /* "result" to be removed */ + return 0; /* read fails */ + } } ]] } Bug{ what = [[wrong code generation for some particular boolean expressions]], report = [[Thierry Van Elsuwe, 2011/01/20]], since = [[5.0]], example = [[ print((('hi' or true) and true) or true) --> hi (should be true) print(((nil and nil) or false) and true) --> nil (should be false) ]], patch = [[ --- lcode.c 2009/06/15 14:12:25 2.25.1.4 +++ lcode.c 2011/01/31 14:44:25 @@ -549,13 +549,6 @@ pc = e->u.s.info; break; } - case VFALSE: { - if (!hasjumps(e)) { - pc = luaK_jump(fs); /* always jump */ - break; - } - /* else go through */ - } default: { pc = jumponcond(fs, e, 0); break; @@ -579,13 +572,6 @@ pc = e->u.s.info; break; } - case VTRUE: { - if (!hasjumps(e)) { - pc = luaK_jump(fs); /* always jump */ - break; - } - /* else go through */ - } default: { pc = jumponcond(fs, e, 1); break; ]] } Bug{ what = [[__newindex metamethod may not work if metatable is its own metatable]], report = [[Cuero Bugot, 2011/08/09]], since = [[5.1]], example = [[ meta={} setmetatable(meta, meta) meta.__newindex = function(t, key, value) print("set") end o = setmetatable({}, meta) o.x = 10 -- should print 'set' ]], patch = [[ --- lvm.c 2009/07/01 21:10:33 2.63.1.4 +++ lvm.c 2011/08/17 20:36:28 @@ -142,6 +142,7 @@ if (!ttisnil(oldval) || /* result is no nil? */ (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */ setobj2t(L, oldval, val); + h->flags = 0; luaC_barriert(L, h, val); return; } ]] } Bug{ what = [[parser may collect a prototype while building it]], report = [[Ingo van Lil, 2011/10/13]], since = [[5.1.4 (caused by patch 5.1.4-6)]], example = nil, patch = [[ --- lparser.c 2007/12/28 15:32:23 2.42.1.3 +++ lparser.c 2011/10/17 13:10:43 @@ -374,9 +374,9 @@ lua_assert(luaG_checkcode(f)); lua_assert(fs->bl == NULL); ls->fs = fs->prev; - L->top -= 2; /* remove table and prototype from the stack */ /* last token read was anchored in defunct function; must reanchor it */ if (fs) anchor_token(ls); + L->top -= 2; /* remove table and prototype from the stack */ } ]] }