lua/bugs

3230 lines
81 KiB
Plaintext

--[=[
** 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 <eof>'
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 = "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 = "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]],
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]],
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 = [[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 = [[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<e; pos++) {
]],
}
Bug{
what = [[lua_setfenv may crash if called over an invalid object]],
report = [[Mike Pall, on 11/2007]],
since = [[5.1]],
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;
]],
}
Bug{
what = [[stand-alone interpreter shows incorrect error message
when the "message" is a coroutine]],
report = [[Patrick Donnelly, on 17/12/2007]],
since = [[5.1]],
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.129 2013/11/08 18:23:16 roberto Exp roberto $
+** $Id: bugs,v 1.129 2013/11/08 18:23:16 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 */
}
]]
}
Bug{
what = [[When loading a file,
Lua may call the reader function again after it returned end of input
]],
report = [[Chris Howie, 2013/06/05]],
since = [[5.1]],
fix = [[5.2]],
example = [[
load(function () print("called"); return nil end)
--> called
--> called (should be called only once!)
]],
patch = [[
--- lzio.h 2007/12/27 13:02:25 1.21.1.1
+++ lzio.h 2013/07/04 13:55:59
@@ -59,6 +59,7 @@
lua_Reader reader;
void* data; /* additional data */
lua_State *L; /* Lua state (for reader) */
+ int eoz; /* true if reader has no more data */
};
--- lzio.c 2007/12/27 13:02:25 1.31.1.1
+++ lzio.c 2013/07/04 13:53:06
@@ -22,10 +22,14 @@
size_t size;
lua_State *L = z->L;
const char *buff;
+ if (z->eoz) return EOZ;
lua_unlock(L);
buff = z->reader(L, z->data, &size);
lua_lock(L);
- if (buff == NULL || size == 0) return EOZ;
+ if (buff == NULL || size == 0) {
+ z->eoz = 1; /* avoid calling reader function next time */
+ return EOZ;
+ }
z->n = size - 1;
z->p = buff;
return char2int(*(z->p++));
@@ -51,6 +55,7 @@
z->data = data;
z->n = 0;
z->p = NULL;
+ z->eoz = 0;
}
]]
}
-----------------------------------------------------------------
-- Lua 5.2.0
Bug{
what = [[memory hoarding when creating Lua hooks for coroutines]],
report = [[Arseny Vakhrushev, 2012/01/16]],
since = [[5.1]],
fix = [[5.2.1]],
example = [[
collectgarbage(); print(collectgarbage'count' * 1024)
for i = 1, 100 do
local co = coroutine.create(function () end)
local x = {}
for j=1,1000 do x[j] = j end
debug.sethook(co, function () return x end, 'l')
end
collectgarbage(); print(collectgarbage'count' * 1024)
-- value should back to near the original level
]],
patch = [[
-- For 5.2
--- ldblib.c 2011/10/24 14:54:05 1.131
+++ ldblib.c 2012/01/18 02:36:59
@@ -253,14 +253,15 @@
}
-#define gethooktable(L) luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY);
+#define gethooktable(L) luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)
static void hookf (lua_State *L, lua_Debug *ar) {
static const char *const hooknames[] =
{"call", "return", "line", "count", "tail call"};
gethooktable(L);
- lua_rawgetp(L, -1, L);
+ lua_pushthread(L);
+ lua_rawget(L, -2);
if (lua_isfunction(L, -1)) {
lua_pushstring(L, hooknames[(int)ar->event]);
if (ar->currentline >= 0)
@@ -306,10 +307,15 @@
count = luaL_optint(L, arg+3, 0);
func = hookf; mask = makemask(smask, count);
}
- gethooktable(L);
+ if (gethooktable(L) == 0) { /* creating hook table? */
+ lua_pushstring(L, "k");
+ lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
+ lua_pushvalue(L, -1);
+ lua_setmetatable(L, -2); /* setmetatable(hooktable) = hooktable */
+ }
+ lua_pushthread(L1); lua_xmove(L1, L, 1);
lua_pushvalue(L, arg+1);
- lua_rawsetp(L, -2, L1); /* set new hook */
- lua_pop(L, 1); /* remove hook table */
+ lua_rawset(L, -3); /* set new hook */
lua_sethook(L1, func, mask, count); /* set hooks */
return 0;
}
@@ -325,7 +331,8 @@
lua_pushliteral(L, "external hook");
else {
gethooktable(L);
- lua_rawgetp(L, -1, L1); /* get hook */
+ lua_pushthread(L1); lua_xmove(L1, L, 1);
+ lua_rawget(L, -2); /* get hook */
lua_remove(L, -2); /* remove hook table */
}
lua_pushstring(L, unmakemask(mask, buff));
]]
}
Bug{
what = [[Lexical gets confused with some combination of arithmetic
operators and hexadecimal numbers]],
report = [[Alexandra Barros, 2012/01/17]],
since = [[5.2.0]],
fix = [[5.2.1]],
example = [[print(0xE+1)]],
patch = [[
--- llex.c 2011/11/30 12:43:51 2.59
+++ llex.c 2012/01/20 18:22:50
@@ -223,12 +223,19 @@
/* LUA_NUMBER */
static void read_numeral (LexState *ls, SemInfo *seminfo) {
+ const char *expo = "Ee";
+ int first = ls->current;
lua_assert(lisdigit(ls->current));
- do {
- save_and_next(ls);
- if (check_next(ls, "EePp")) /* exponent part? */
+ save_and_next(ls);
+ if (first == '0' && check_next(ls, "Xx")) /* hexadecimal? */
+ expo = "Pp";
+ for (;;) {
+ if (check_next(ls, expo)) /* exponent part? */
check_next(ls, "+-"); /* optional exponent sign */
- } while (lislalnum(ls->current) || ls->current == '.');
+ if (lisxdigit(ls->current) || ls->current == '.')
+ save_and_next(ls);
+ else break;
+ }
save(ls, '\0');
buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */
if (!buff2d(ls->buff, &seminfo->r)) /* format error? */
]]
}
Bug{
what = [[Finalizers may call functions from a dynamic library after
the library has been unloaded]],
report = [[Josh Haberman, 2012/04/08]],
since = [[5.1]],
fix = [[5.2.1]],
example = [[
local u = setmetatable({}, {__gc = function () foo() end})
local m = require 'mod' -- 'mod' may be any dynamic library written in C
foo = m.foo -- 'foo' may be any function from 'mod'
-- end program; it crashes
]],
patch = [[
loadlib.c:
95c95
< #define LIBPREFIX "LOADLIB: "
---
> #define CLIBS "_CLIBS"
251,266c251,256
<
< static void **ll_register (lua_State *L, const char *path) {
< void **plib;
< lua_pushfstring(L, "%s%s", LIBPREFIX, path);
< lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
< if (!lua_isnil(L, -1)) /* is there an entry? */
< plib = (void **)lua_touserdata(L, -1);
< else { /* no entry yet; create one */
< lua_pop(L, 1); /* remove result from gettable */
< plib = (void **)lua_newuserdata(L, sizeof(const void *));
< *plib = NULL;
< luaL_setmetatable(L, "_LOADLIB");
< lua_pushfstring(L, "%s%s", LIBPREFIX, path);
< lua_pushvalue(L, -2);
< lua_settable(L, LUA_REGISTRYINDEX);
< }
---
> static void *ll_checkclib (lua_State *L, const char *path) {
> void *plib;
> lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
> lua_getfield(L, -1, path);
> plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */
> lua_pop(L, 2); /* pop CLIBS table and 'plib' */
270a261,270
> static void ll_addtoclib (lua_State *L, const char *path, void *plib) {
> lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
> lua_pushlightuserdata(L, plib);
> lua_pushvalue(L, -1);
> lua_setfield(L, -3, path); /* CLIBS[path] = plib */
> lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */
> lua_pop(L, 1); /* pop CLIBS table */
> }
>
>
272,273c272,273
< ** __gc tag method: calls library's `ll_unloadlib' function with the lib
< ** handle
---
> ** __gc tag method for CLIBS table: calls 'll_unloadlib' for all lib
> ** handles in list CLIBS
276,278c276,281
< void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
< if (*lib) ll_unloadlib(*lib);
< *lib = NULL; /* mark library as closed */
---
> int n = luaL_len(L, 1);
> for (; n >= 1; n--) { /* for each handle, in reverse order */
> lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */
> ll_unloadlib(lua_touserdata(L, -1));
> lua_pop(L, 1); /* pop handle */
> }
284,286c287,292
< void **reg = ll_register(L, path);
< if (*reg == NULL) *reg = ll_load(L, path, *sym == '*');
< if (*reg == NULL) return ERRLIB; /* unable to load library */
---
> void *reg = ll_checkclib(L, path); /* check loaded C libraries */
> if (reg == NULL) { /* must load library? */
> reg = ll_load(L, path, *sym == '*');
> if (reg == NULL) return ERRLIB; /* unable to load library */
> ll_addtoclib(L, path, reg);
> }
292c298
< lua_CFunction f = ll_sym(L, *reg, sym);
---
> lua_CFunction f = ll_sym(L, reg, sym);
675,676c681,683
< /* create new type _LOADLIB */
< luaL_newmetatable(L, "_LOADLIB");
---
> /* create table CLIBS to keep track of loaded C libraries */
> luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS);
> lua_createtable(L, 0, 1); /* metatable for CLIBS */
678a686
> lua_setmetatable(L, -2);
]]
}
Bug{
what = [[wrong handling of 'nCcalls' in coroutines]],
report = [[Alexander Gavrilov, 2012/04/18]],
since = [[5.2.0]],
fix = [[5.2.1]],
example = [[
coroutine.wrap(function()
print(pcall(pcall,pcall,pcall,pcall,pcall,error,3))
end)()
]],
patch = [[
--- ldo.c 2011/11/29 15:55:08 2.102
+++ ldo.c 2012/04/26 20:38:32
@@ -402,8 +402,6 @@
int n;
lua_assert(ci->u.c.k != NULL); /* must have a continuation */
lua_assert(L->nny == 0);
- /* finish 'luaD_call' */
- L->nCcalls--;
/* finish 'lua_callk' */
adjustresults(L, ci->nresults);
/* call continuation function */
@@ -513,7 +511,6 @@
api_checknelems(L, n);
firstArg = L->top - n; /* yield results come from continuation */
}
- L->nCcalls--; /* finish 'luaD_call' */
luaD_poscall(L, firstArg); /* finish 'luaD_precall' */
}
unroll(L, NULL);
]]
}
Bug{
what = [[Internal Lua values may escape through the debug API]],
report = [[Dan Tull, 2012/04/20]],
since = [[5.1]],
fix = [[5.2.1]],
example = [[
-- for Lua 5.1
local firsttime = true
local function foo ()
if firsttime then
firsttime = false
return "a = 1"
else
for i = 1, 10 do
print(debug.getlocal(2, i))
end
end
end
print(load(foo)) -- prints some lines and then seg. fault.
]],
patch = [[
]]
}
Bug{
what = [[Problems when yielding from debug hooks]],
report = [[Erik Cassel, 2012/06/05]],
since = [[5.2.0]],
fix = [[5.2.1]],
example = [[
Set, in C, a line hook that simply yields,
and then call any Lua function.
You get an infinite loop of yields.
]],
patch = [[
]]
}
-----------------------------------------------------------------
-- Lua 5.2.1
Bug{
what = [[Some patterns can overflow the C stack, due to recursion]],
report = [[Tim Starling, 2012/07/08]],
since = [[2.5]],
example = [[print(string.find(string.rep("a", 2^20), string.rep(".?", 2^20)))]],
patch = [[
]]
}
Bug{
what = [['pcall' may not restore previous error function when
inside coroutines]],
report = [[Alexander Gavrilov, 2012/06/12]],
since = [[5.2.0]],
fix = nil,
example = [[
function errfunc(x)
return 'errfunc'
end
function test(do_yield)
print(do_yield and "yielding" or "not yielding")
pcall(function() -- this pcall sets errfunc back to none
if do_yield then
coroutine.yield() -- stops errfunc from being restored
end
end)
error('fail!')
end
coro = coroutine.wrap(function()
print(xpcall(test, errfunc, false))
print(xpcall(test, errfunc, true))
print(xpcall(test, errfunc, false))
end)
coro()
--> not yielding
--> false errfunc
--> yielding
coro()
--> false temp:12: fail! <<<< should be 'errfunc' too
--> not yielding
--> false errfunc
]],
patch = [[
--- ldo.c 2012/08/28 18:30:45 2.107
+++ ldo.c 2012/09/23 15:49:55
@@ -403,7 +403,11 @@
int n;
lua_assert(ci->u.c.k != NULL); /* must have a continuation */
lua_assert(L->nny == 0);
- /* finish 'lua_callk' */
+ if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */
+ ci->callstatus &= ~CIST_YPCALL; /* finish 'lua_pcall' */
+ L->errfunc = ci->u.c.old_errfunc;
+ }
+ /* finish 'lua_callk'/'lua_pcall' */
adjustresults(L, ci->nresults);
/* call continuation function */
if (!(ci->callstatus & CIST_STAT)) /* no call status? */
]]
}
Bug{
what = [[Check for garbage collector in function calls does not cover
all paths]],
report = [[Roberto, 2012/08/15]],
since = [[5.2.1]],
fix = nil,
example = [[
See <a href="http://lua-users.org/lists/lua-l/2012-08/msg00149.html">
http://lua-users.org/lists/lua-l/2012-08/msg00149.html</a>
]],
patch = [[
@@ -311,6 +311,7 @@
ci->top = L->top + LUA_MINSTACK;
lua_assert(ci->top <= L->stack_last);
ci->callstatus = 0;
+ luaC_checkGC(L); /* stack grow uses memory */
if (L->hookmask & LUA_MASKCALL)
luaD_hook(L, LUA_HOOKCALL, -1);
lua_unlock(L);
@@ -338,6 +339,7 @@
ci->u.l.savedpc = p->code; /* starting point */
ci->callstatus = CIST_LUA;
L->top = ci->top;
+ luaC_checkGC(L); /* stack grow uses memory */
if (L->hookmask & LUA_MASKCALL)
callhook(L, ci);
return 0;
@@ -393,7 +395,6 @@
luaV_execute(L); /* call it */
if (!allowyield) L->nny--;
L->nCcalls--;
- luaC_checkGC(L);
}
]]
}
Bug{
what = [[load/loadfile returns wrong result when given an environment
for a binary chunk with no upvalues]],
report = [[Vladimir Strakh, 2012/11/28]],
since = [[5.2.0]],
fix = nil,
example = [[
f = load(string.dump(function () return 1 end), nil, "b", {})
print(type(f)) --> table (whould be a function)
]],
patch = [[
--- lbaselib.c 2012/04/27 14:13:19 1.274
+++ lbaselib.c 2012/12/03 20:08:15
@@ -244,5 +244,11 @@
-static int load_aux (lua_State *L, int status) {
- if (status == LUA_OK)
+static int load_aux (lua_State *L, int status, int envidx) {
+ if (status == LUA_OK) {
+ if (envidx != 0) { /* 'env' parameter? */
+ lua_pushvalue(L, envidx); /* environment for loaded function */
+ if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
+ lua_pop(L, 1); /* remove 'env' if not used by previous call */
+ }
return 1;
+ }
else {
@@ -258,9 +264,5 @@
const char *mode = luaL_optstring(L, 2, NULL);
- int env = !lua_isnone(L, 3); /* 'env' parameter? */
+ int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */
int status = luaL_loadfilex(L, fname, mode);
- if (status == LUA_OK && env) { /* 'env' parameter? */
- lua_pushvalue(L, 3);
- lua_setupvalue(L, -2, 1); /* set it as 1st upvalue of loaded chunk */
- }
- return load_aux(L, status);
+ return load_aux(L, status, env);
}
@@ -309,5 +311,5 @@
size_t l;
- int top = lua_gettop(L);
const char *s = lua_tolstring(L, 1, &l);
const char *mode = luaL_optstring(L, 3, "bt");
+ int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */
if (s != NULL) { /* loading a string? */
@@ -322,7 +324,3 @@
}
- if (status == LUA_OK && top >= 4) { /* is there an 'env' argument */
- lua_pushvalue(L, 4); /* environment for loaded function */
- lua_setupvalue(L, -2, 1); /* set it as 1st upvalue */
- }
- return load_aux(L, status);
+ return load_aux(L, status, env);
}
]]
}
Bug{
what = [[stack overflow in vararg functions with many fixed
parameters called with few arguments]],
report = [[云风, 2013/04/17]],
since = [[5.1]],
fix = nil,
example = [[
function f(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
p11, p12, p13, p14, p15, p16, p17, p18, p19, p20,
p21, p22, p23, p24, p25, p26, p27, p28, p29, p30,
p31, p32, p33, p34, p35, p36, p37, p38, p39, p40,
p41, p42, p43, p44, p45, p46, p48, p49, p50, ...)
local a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14
end
f() -- seg. fault (on some machines)
]],
patch = [[
--- ldo.c 2012/10/01 14:05:04 2.108
+++ ldo.c 2013/04/19 20:56:06
@@ -324,7 +324,7 @@
case LUA_TLCL: { /* Lua function: prepare its call */
StkId base;
Proto *p = clLvalue(func)->p;
- luaD_checkstack(L, p->maxstacksize);
+ luaD_checkstack(L, p->maxstacksize + p->numparams);
func = restorestack(L, funcr);
n = cast_int(L->top - func) - 1; /* number of real arguments */
for (; n < p->numparams; n++)
]],
}
--[=[
Bug{
what = [[garbage collector can trigger too many times in recursive loops]],
report = [[Roberto, 2013/04/25]],
since = [[5.2.2]],
fix = nil,
example = [[
function f() f() end
f() -- it takes too long before a "stack overflow" error
]],
patch = [[
--- lgc.c 2013/04/12 18:48:47 2.140.1.1
+++ lgc.c 2013/04/25 21:30:20
@@ -495,2 +495,3 @@
static lu_mem traversestack (global_State *g, lua_State *th) {
+ int n = 0;
StkId o = th->stack;
@@ -505,3 +506,9 @@
}
- return sizeof(lua_State) + sizeof(TValue) * th->stacksize;
+ else { /* count call infos to compute size */
+ CallInfo *ci;
+ for (ci = &th->base_ci; ci != th->ci; ci = ci->next)
+ n++;
+ }
+ return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
+ sizeof(CallInfo) * n;
}
]]
}
Bug{
what = [[Wrong assert when reporting concatenation errors
(manifests only when Lua is compiled in debug mode)]],
report = [[Roberto, 2013/05/05]],
since = [[?]],
fix = nil,
example = [[
-- only with Lua compiled in debug mode
print({} .. 2)
]],
patch = [[
--- ldebug.c 2013/04/12 18:48:47 2.90.1.1
+++ ldebug.c 2013/05/05 14:38:30
@@ -519,5 +519,5 @@
l_noret luaG_concaterror (lua_State *L, StkId p1, StkId p2) {
if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;
- lua_assert(!ttisstring(p1) && !ttisnumber(p2));
+ lua_assert(!ttisstring(p1) && !ttisnumber(p1));
luaG_typeerror(L, p1, "concatenate");
}
]]
}
Bug{
what = [[Wrong error message in some short-cut expressions]],
report = [[Egor Skriptunoff, 2013/05/10]],
since = [[5.0]],
fix = nil,
example = [[
> a,b,c = true,true,true
> (a and b or c)('', '')
stdin:1: attempt to call a boolean value (global 'c')
(It should be global 'b' instead of 'c'.)
]],
patch = [[
--- ldebug.c 2013/05/06 17:20:22 2.90.1.2
+++ ldebug.c 2013/05/14 19:52:48
@@ -327,12 +327,20 @@
}
+static int filterpc (int pc, int jmptarget) {
+ if (pc < jmptarget) /* is code conditional (inside a jump)? */
+ return -1; /* cannot know who sets that register */
+ else return pc; /* current position sets that register */
+}
+
+
/*
** try to find last instruction before 'lastpc' that modified register 'reg'
*/
static int findsetreg (Proto *p, int lastpc, int reg) {
int pc;
int setreg = -1; /* keep last instruction that changed 'reg' */
+ int jmptarget = 0; /* any code before this address is conditional */
for (pc = 0; pc < lastpc; pc++) {
Instruction i = p->code[pc];
OpCode op = GET_OPCODE(i);
@@ -341,33 +349,38 @@
case OP_LOADNIL: {
int b = GETARG_B(i);
if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */
- setreg = pc;
+ setreg = filterpc(pc, jmptarget);
break;
}
case OP_TFORCALL: {
- if (reg >= a + 2) setreg = pc; /* affect all regs above its base */
+ if (reg >= a + 2) /* affect all regs above its base */
+ setreg = filterpc(pc, jmptarget);
break;
}
case OP_CALL:
case OP_TAILCALL: {
- if (reg >= a) setreg = pc; /* affect all registers above base */
+ if (reg >= a) /* affect all registers above base */
+ setreg = filterpc(pc, jmptarget);
break;
}
case OP_JMP: {
int b = GETARG_sBx(i);
int dest = pc + 1 + b;
/* jump is forward and do not skip `lastpc'? */
- if (pc < dest && dest <= lastpc)
- pc += b; /* do the jump */
+ if (pc < dest && dest <= lastpc) {
+ if (dest > jmptarget)
+ jmptarget = dest; /* update 'jmptarget' */
+ }
break;
}
case OP_TEST: {
- if (reg == a) setreg = pc; /* jumped code can change 'a' */
+ if (reg == a) /* jumped code can change 'a' */
+ setreg = filterpc(pc, jmptarget);
break;
}
default:
if (testAMode(op) && reg == a) /* any instruction that set A */
- setreg = pc;
+ setreg = filterpc(pc, jmptarget);
break;
}
}
]]
}
Bug{
what = [[luac listings choke on long strings]],
report = [[Ashwin Hirschi, 2013/07/03]],
since = [[5.1.2]],
fix = nil,
example = [[
-- When you call 'luac -l' over this chunk, it chokes the output
s="Lorem ipsum dolor sit amet, consectetur, "
]],
patch = [[
--- luac.c 2011-11-29 15:46:33 -0200 1.69
+++ luac.c 2013-07-03 21:26:01 -0300
@@ -251,7 +251,7 @@
static void PrintConstant(const Proto* f, int i)
{
const TValue* o=&f->k[i];
- switch (ttype(o))
+ switch (ttypenv(o))
{
case LUA_TNIL:
printf("nil");
]]
}
Bug{
what = [[GC can collect a long string still in use during parser]],
report = [[Roberto, 2013/08/30]],
since = [[5.2]],
fix = nil,
example = [[This bug is very difficult to happen (and to reproduce),
because it depends on the GC running in a very specific way when
parsing a source code with long (larger than 40 characters) identifiers.]],
patch = [[
--- ltable.h 2013/04/12 18:48:47 2.16.1.1
+++ ltable.h 2013/08/30 15:34:24
@@ -18,4 +18,8 @@
#define invalidateTMcache(t) ((t)->flags = 0)
+/* returns the key, given the value of a table entry */
+#define keyfromval(v) \
+ (gkey(cast(Node *, cast(char *, (v)) - offsetof(Node, i_val))))
+
LUAI_FUNC const TValue *luaH_getint (Table *t, int key);
--- llex.c 2013/04/12 18:48:47 2.63.1.1
+++ llex.c 2013/08/30 15:34:59
@@ -134,4 +134,7 @@
luaC_checkGC(L);
}
+ else { /* string already present */
+ ts = rawtsvalue(keyfromval(o)); /* re-use value previously stored */
+ }
L->top--; /* remove string from stack */
return ts;
]]
}
]
Bug{
what = [[Call to macro 'luai_userstateclose' should be done only
after the calls to __gc methods.]],
report = [[Jean-Luc Jumpertz, 2013/09/02]],
since = [[ ]],
fix = nil,
example = [[No example]],
patch = [[
--- lstate.c 2013/04/12 18:48:47 2.99.1.1
+++ lstate.c 2013/11/08 17:39:57
@@ -194,2 +194,4 @@
g->gcrunning = 1; /* allow gc */
+ g->version = lua_version(NULL);
+ luai_userstateopen(L);
}
@@ -224,2 +226,4 @@
luaC_freeallobjects(L); /* collect all objects */
+ if (g->version) /* closing a fully built state? */
+ luai_userstateclose(L);
luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
@@ -289,3 +293,3 @@
g->panic = NULL;
- g->version = lua_version(NULL);
+ g->version = NULL;
g->gcstate = GCSpause;
@@ -308,4 +312,2 @@
}
- else
- luai_userstateopen(L);
return L;
@@ -317,3 +319,2 @@
lua_lock(L);
- luai_userstateclose(L);
close_state(L);
]]
}
Bug{
what = [[Resuming the running coroutine makes it unyieldable]],
report = [[Florian Nücke, 2013/10/28]],
since = [[5.2]],
fix = nil,
example = [[
-- should print 'true'
print(coroutine.resume(coroutine.create(function()
coroutine.resume(coroutine.running())
coroutine.yield()
end)))
]],
patch = [[
--- ldo.c 2013/04/19 21:03:23 2.108.1.2
+++ ldo.c 2013/11/08 18:20:57
@@ -536,2 +536,3 @@
int status;
+ int oldnny = L->nny; /* save 'nny' */
lua_lock(L);
@@ -557,3 +558,3 @@
}
- L->nny = 1; /* do not allow yields */
+ L->nny = oldnny; /* restore 'nny' */
L->nCcalls--;
]]
}
Bug{
what = [[compiler can optimize away overflow check in 'table.concat']],
report = [[Paige DePol, 2014/03/30]],
since = [[5.1 (at least)]],
fix = nil,
example = [[
> unpack({}, 0, 2^31 - 1)
(segfaults on some platforms with some compiler options)
]],
patch = [[
]]
}
--[=[
Bug{
what = [[ ]],
report = [[ ]],
since = [[ ]],
fix = nil,
example = [[ ]],
patch = [[
]]
}
]=]