mirror of
https://github.com/lua/lua
synced 2024-11-22 21:01:26 +03:00
57f5b81da9
A long bracket with too many equal signs can overflow the 'int' used for the counting and some arithmetic done on the value. Changing the counter to 'size_t' avoids that. (Because what is counted goes to a buffer, an overflow in the counter will first raise a buffer-overflow error.)
4053 lines
102 KiB
Plaintext
4053 lines
102 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
|
|
@@ -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]],
|
|
fix = [[5.2.2]],
|
|
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 = [[5.2.2]],
|
|
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 = [[5.2.2]],
|
|
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 = [[5.2.2]],
|
|
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 = [[Lua does not check memory use when creating error messages]],
|
|
report = [[John Dunn, 2012/09/24]],
|
|
since = [[5.2.0]],
|
|
fix = nil,
|
|
example = [[
|
|
local code = "function test()\n bob.joe.larry = 23\n end"
|
|
|
|
load(code)()
|
|
|
|
-- memory will grow steadly
|
|
for i = 1, math.huge do
|
|
pcall(test)
|
|
if i % 100000 == 0 then
|
|
io.write(collectgarbage'count'*1024, "\n")
|
|
end
|
|
end
|
|
]],
|
|
patch = [[
|
|
]]
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-----------------------------------------------------------------
|
|
-- Lua 5.2.2
|
|
|
|
|
|
Bug{
|
|
what = [[stack overflow in vararg functions with many fixed
|
|
parameters called with few arguments]],
|
|
report = [[云风, 2013/04/17]],
|
|
since = [[5.1]],
|
|
fix = [[5.2.3]],
|
|
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 = [[5.2.3]],
|
|
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 = [[5.2.3]],
|
|
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 = [[5.2.3]],
|
|
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 = [[5.2.3]],
|
|
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 = [[5.2.3]],
|
|
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 = [[5.2.3]],
|
|
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--;
|
|
]]
|
|
}
|
|
|
|
|
|
|
|
-----------------------------------------------------------------
|
|
-- Lua 5.2.3
|
|
|
|
Bug{
|
|
what = [[compiler can optimize away overflow check in 'table.unpack']],
|
|
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 = [[
|
|
--- ltablib.c 2013/04/12 18:48:47 1.65.1.1
|
|
+++ ltablib.c 2014/05/07 16:32:55 1.65.1.2
|
|
@@ -134,13 +135,14 @@
|
|
|
|
|
|
static int unpack (lua_State *L) {
|
|
- int i, e, n;
|
|
+ int i, e;
|
|
+ unsigned int n;
|
|
luaL_checktype(L, 1, LUA_TTABLE);
|
|
i = luaL_optint(L, 2, 1);
|
|
e = luaL_opt(L, luaL_checkint, 3, luaL_len(L, 1));
|
|
if (i > e) return 0; /* empty range */
|
|
- n = e - i + 1; /* number of elements */
|
|
- if (n <= 0 || !lua_checkstack(L, n)) /* n <= 0 means arith. overflow */
|
|
+ n = (unsigned int)e - (unsigned int)i; /* number of elements minus 1 */
|
|
+ if (n > (INT_MAX - 10) || !lua_checkstack(L, ++n))
|
|
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] */
|
|
]]
|
|
}
|
|
|
|
Bug{
|
|
what = [[Ephemeron table can wrongly collect entry with strong key]],
|
|
report = [[Jörg Richter, 2014/08/22]],
|
|
since = [[5.2]],
|
|
fix = nil,
|
|
example = [[
|
|
(This bug is very hard to reproduce,
|
|
because it depends on a specific interleaving of
|
|
events between the incremental collector and the program.)
|
|
]],
|
|
patch = [[
|
|
--- lgc.c 2013/04/26 18:22:05 2.140.1.2
|
|
+++ lgc.c 2014/09/01 13:24:33
|
|
@@ -403,7 +403,7 @@
|
|
reallymarkobject(g, gcvalue(gval(n))); /* mark it now */
|
|
}
|
|
}
|
|
- if (prop)
|
|
+ if (g->gcstate != GCSatomic || prop)
|
|
linktable(h, &g->ephemeron); /* have to propagate again */
|
|
else if (hasclears) /* does table have white keys? */
|
|
linktable(h, &g->allweak); /* may have to clean white keys */
|
|
]]
|
|
}
|
|
|
|
Bug{
|
|
what = [[Chunk with too many lines can seg. fault]],
|
|
report = [[Roberto, 2014/11/14]],
|
|
since = [[5.1 (at least)]],
|
|
fix = nil,
|
|
example = [[
|
|
-- the cause of the bug is the use of an unitialized variable, so
|
|
-- it cannot be reproduced reliably
|
|
local s = string.rep("\n", 2^24)
|
|
print(load(function () return s end))
|
|
]],
|
|
patch = [[
|
|
--- llex.c 2013/08/30 15:49:41 2.63.1.2
|
|
+++ llex.c 2015/02/09 17:05:31
|
|
@@ -153,5 +153,5 @@
|
|
next(ls); /* skip `\n\r' or `\r\n' */
|
|
if (++ls->linenumber >= MAX_INT)
|
|
- luaX_syntaxerror(ls, "chunk has too many lines");
|
|
+ lexerror(ls, "chunk has too many lines", 0);
|
|
}
|
|
|
|
]]
|
|
}
|
|
|
|
|
|
-----------------------------------------------------------------
|
|
-- Lua 5.3.0
|
|
|
|
Bug{
|
|
what = [['string.format("%f")' can cause a buffer overflow
|
|
(only when 'lua_Number' is long double!)]],
|
|
report = [[Roberto, 2015/01/13]],
|
|
since = [[5.3]],
|
|
fix = nil,
|
|
example = [[string.format("%.99f", 1e4000) -- when floats are long double]],
|
|
patch = [[
|
|
--- lstrlib.c 2014/12/11 14:03:07 1.221
|
|
+++ lstrlib.c 2015/02/23 19:01:42
|
|
@@ -800,3 +800,4 @@
|
|
/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
|
|
-#define MAX_ITEM 512
|
|
+#define MAX_ITEM \
|
|
+ (sizeof(lua_Number) <= 4 ? 150 : sizeof(lua_Number) <= 8 ? 450 : 5050)
|
|
|
|
]]
|
|
}
|
|
|
|
Bug{
|
|
what = [['debug.getlocal' on a coroutine suspended in a hook
|
|
can crash the interpreter]],
|
|
report = [[云风, 2015/02/11]],
|
|
since = [[5.2]],
|
|
fix = nil,
|
|
example = [[see http://lua-users.org/lists/lua-l/2015-02/msg00146.html]],
|
|
patch = [[
|
|
--- ldebug.c 2015/01/02 12:52:22 2.110
|
|
+++ ldebug.c 2015/02/13 16:03:23
|
|
@@ -49,4 +49,14 @@
|
|
|
|
|
|
+static void swapextra (lua_State *L) {
|
|
+ if (L->status == LUA_YIELD) {
|
|
+ CallInfo *ci = L->ci; /* get function that yielded */
|
|
+ StkId temp = ci->func; /* exchange its 'func' and 'extra' values */
|
|
+ ci->func = restorestack(L, ci->extra);
|
|
+ ci->extra = savestack(L, temp);
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
/*
|
|
** this function can be called asynchronous (e.g. during a signal)
|
|
@@ -145,4 +155,5 @@
|
|
const char *name;
|
|
lua_lock(L);
|
|
+ swapextra(L);
|
|
if (ar == NULL) { /* information about non-active function? */
|
|
if (!isLfunction(L->top - 1)) /* not a Lua function? */
|
|
@@ -159,4 +170,5 @@
|
|
}
|
|
}
|
|
+ swapextra(L);
|
|
lua_unlock(L);
|
|
return name;
|
|
@@ -166,10 +178,13 @@
|
|
LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
|
|
StkId pos = 0; /* to avoid warnings */
|
|
- const char *name = findlocal(L, ar->i_ci, n, &pos);
|
|
+ const char *name;
|
|
lua_lock(L);
|
|
+ swapextra(L);
|
|
+ name = findlocal(L, ar->i_ci, n, &pos);
|
|
if (name) {
|
|
setobjs2s(L, pos, L->top - 1);
|
|
L->top--; /* pop value */
|
|
}
|
|
+ swapextra(L);
|
|
lua_unlock(L);
|
|
return name;
|
|
@@ -271,4 +286,5 @@
|
|
StkId func;
|
|
lua_lock(L);
|
|
+ swapextra(L);
|
|
if (*what == '>') {
|
|
ci = NULL;
|
|
@@ -289,4 +305,5 @@
|
|
api_incr_top(L);
|
|
}
|
|
+ swapextra(L);
|
|
if (strchr(what, 'L'))
|
|
collectvalidlines(L, cl);
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [[suspended '__le' metamethod can give wrong result]],
|
|
report = [[Eric Zhong, 2015/04/07]],
|
|
since = [[5.2]],
|
|
fix = nil,
|
|
|
|
example = [[
|
|
mt = {__le = function (a,b) coroutine.yield("yield"); return a.x <= b.x end}
|
|
t1 = setmetatable({x=1}, mt)
|
|
t2 = {x=2}
|
|
co = coroutine.wrap(function (a,b) return t2 <= t1 end)
|
|
co()
|
|
print(co()) --> true (should be false)
|
|
]],
|
|
|
|
patch = [[
|
|
--- lstate.h 2014/10/30 18:53:28 2.119
|
|
+++ lstate.h 2015/04/13 15:58:40
|
|
@@ -94,6 +94,7 @@
|
|
#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */
|
|
#define CIST_TAIL (1<<5) /* call was tail called */
|
|
#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
|
|
+#define CIST_LEQ (1<<7) /* using __lt for __le */
|
|
|
|
#define isLua(ci) ((ci)->callstatus & CIST_LUA)
|
|
|
|
--- lvm.c 2014/12/27 20:30:38 2.232
|
|
+++ lvm.c 2015/04/13 15:51:30
|
|
@@ -292,9 +292,14 @@
|
|
return l_strcmp(tsvalue(l), tsvalue(r)) <= 0;
|
|
else if ((res = luaT_callorderTM(L, l, r, TM_LE)) >= 0) /* first try 'le' */
|
|
return res;
|
|
- else if ((res = luaT_callorderTM(L, r, l, TM_LT)) < 0) /* else try 'lt' */
|
|
- luaG_ordererror(L, l, r);
|
|
- return !res;
|
|
+ else { /* try 'lt': */
|
|
+ L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
|
|
+ res = luaT_callorderTM(L, r, l, TM_LT);
|
|
+ L->ci->callstatus ^= CIST_LEQ; /* clear mark */
|
|
+ if (res < 0)
|
|
+ luaG_ordererror(L, l, r);
|
|
+ return !res; /* result is negated */
|
|
+ }
|
|
}
|
|
|
|
|
|
@@ -553,11 +558,11 @@
|
|
case OP_LE: case OP_LT: case OP_EQ: {
|
|
int res = !l_isfalse(L->top - 1);
|
|
L->top--;
|
|
- /* metamethod should not be called when operand is K */
|
|
- lua_assert(!ISK(GETARG_B(inst)));
|
|
- if (op == OP_LE && /* "<=" using "<" instead? */
|
|
- ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE)))
|
|
- res = !res; /* invert result */
|
|
+ if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
|
|
+ lua_assert(op == OP_LE);
|
|
+ ci->callstatus ^= CIST_LEQ; /* clear mark */
|
|
+ res = !res; /* negate result */
|
|
+ }
|
|
lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
|
|
if (res != GETARG_A(inst)) /* condition failed? */
|
|
ci->u.l.savedpc++; /* skip jump instruction */
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [[return hook may not see correct values for
|
|
active local variables when function returns]],
|
|
report = [[Philipp Janda/Peng Yi, 2015/05/19]],
|
|
since = [[5.0]],
|
|
fix = nil,
|
|
example = [[
|
|
see messasge http://lua-users.org/lists/lua-l/2015-05/msg00376.html]],
|
|
patch = [[
|
|
]]
|
|
}
|
|
|
|
|
|
|
|
-----------------------------------------------------------------
|
|
-- Lua 5.3.1
|
|
|
|
Bug{
|
|
what = [['io.lines' does not check maximum number of options]],
|
|
report = [[Patrick Donnell, 2015/07/10]],
|
|
since = [[5.3.0]],
|
|
fix = nil,
|
|
example = [[
|
|
-- can segfault in some machines
|
|
t ={}; for i = 1, 253 do t[i] = 1 end
|
|
io.lines("someexistingfile", table.unpack(t))()
|
|
]],
|
|
patch = [[
|
|
--- liolib.c 2015/07/07 17:03:34 2.146
|
|
+++ liolib.c 2015/07/15 14:40:28 2.147
|
|
@@ -318,8 +318,15 @@
|
|
static int io_readline (lua_State *L);
|
|
|
|
|
|
+/*
|
|
+** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit
|
|
+** in the limit for upvalues of a closure)
|
|
+*/
|
|
+#define MAXARGLINE 250
|
|
+
|
|
static void aux_lines (lua_State *L, int toclose) {
|
|
int n = lua_gettop(L) - 1; /* number of arguments to read */
|
|
+ luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments");
|
|
lua_pushinteger(L, n); /* number of arguments to read */
|
|
lua_pushboolean(L, toclose); /* close/not close file when finished */
|
|
lua_rotate(L, 2, 2); /* move 'n' and 'toclose' to their positions */
|
|
]]
|
|
}
|
|
|
|
|
|
-----------------------------------------------------------------
|
|
-- Lua 5.3.2
|
|
|
|
Bug{
|
|
what = [[Metatable may access its own dealocated field when
|
|
it has a self reference in __newindex]],
|
|
report = [[actboy168@gmail.com, 2016/01/01]],
|
|
since = [[5.3.2]],
|
|
fix = nil,
|
|
example = [[
|
|
local mt = {}
|
|
mt.__newindex = mt
|
|
local t = setmetatable({}, mt)
|
|
t[1] = 1 -- will segfault on some machines
|
|
]],
|
|
patch = [[
|
|
--- lvm.c 2015/11/23 11:30:45 2.265
|
|
+++ lvm.c 2016/01/01 14:34:12
|
|
@@ -190,18 +190,19 @@
|
|
for (loop = 0; loop < MAXTAGLOOP; loop++) {
|
|
const TValue *tm;
|
|
if (oldval != NULL) {
|
|
- lua_assert(ttistable(t) && ttisnil(oldval));
|
|
+ Table *h = hvalue(t); /* save 't' table */
|
|
+ lua_assert(ttisnil(oldval));
|
|
/* must check the metamethod */
|
|
- if ((tm = fasttm(L, hvalue(t)->metatable, TM_NEWINDEX)) == NULL &&
|
|
+ if ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL &&
|
|
/* no metamethod; is there a previous entry in the table? */
|
|
(oldval != luaO_nilobject ||
|
|
/* no previous entry; must create one. (The next test is
|
|
always true; we only need the assignment.) */
|
|
- (oldval = luaH_newkey(L, hvalue(t), key), 1))) {
|
|
+ (oldval = luaH_newkey(L, h, key), 1))) {
|
|
/* no metamethod and (now) there is an entry with given key */
|
|
setobj2t(L, cast(TValue *, oldval), val);
|
|
- invalidateTMcache(hvalue(t));
|
|
- luaC_barrierback(L, hvalue(t), val);
|
|
+ invalidateTMcache(h);
|
|
+ luaC_barrierback(L, h, val);
|
|
return;
|
|
}
|
|
/* else will try the metamethod */
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [[label between local definitions can mix-up their initializations]],
|
|
report = [[Karel Tuma, 2016/03/01]],
|
|
since = [[5.2]],
|
|
fix = nil,
|
|
example = [[
|
|
do
|
|
local k = 0
|
|
local x
|
|
::foo::
|
|
local y -- should be reset to nil after goto, but it is not
|
|
assert(not y)
|
|
y = true
|
|
k = k + 1
|
|
if k < 2 then goto foo end
|
|
end
|
|
]],
|
|
patch = [[
|
|
--- lparser.c 2015/11/02 16:09:30 2.149
|
|
+++ lparser.c 2016/03/03 12:03:37
|
|
@@ -1226,7 +1226,7 @@
|
|
checkrepeated(fs, ll, label); /* check for repeated labels */
|
|
checknext(ls, TK_DBCOLON); /* skip double colon */
|
|
/* create new entry for this label */
|
|
- l = newlabelentry(ls, ll, label, line, fs->pc);
|
|
+ l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs));
|
|
skipnoopstat(ls); /* skip other no-op statements */
|
|
if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */
|
|
/* assume that locals are already out of scope */
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [['gmatch' iterator fails when called from a coroutine different
|
|
from the one that created it]],
|
|
report = [[Nagaev Boris, 2016/03/18]],
|
|
since = [[5.3.2]],
|
|
fix = nil,
|
|
example = [[
|
|
local f = string.gmatch("1 2 3 4 5", "%d+")
|
|
print(f()) --> 1
|
|
co = coroutine.wrap(f)
|
|
print(co()) --> ??? (should be 2)
|
|
]],
|
|
patch = [[
|
|
--- lstrlib.c 2015/11/25 16:28:17 1.239
|
|
+++ lstrlib.c 2016/04/11 15:29:41
|
|
@@ -688,6 +688,7 @@
|
|
static int gmatch_aux (lua_State *L) {
|
|
GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3));
|
|
const char *src;
|
|
+ gm->ms.L = L;
|
|
for (src = gm->src; src <= gm->ms.src_end; src++) {
|
|
const char *e;
|
|
reprepstate(&gm->ms);
|
|
]]
|
|
}
|
|
|
|
|
|
-----------------------------------------------------------------
|
|
-- Lua 5.3.3
|
|
|
|
|
|
Bug{
|
|
what = [[expression list with four or more expressions in
|
|
a 'for' loop can crash the interpreter]],
|
|
report = [[Marco Schöpl, 2016/06/17]],
|
|
since = [[5.2]],
|
|
fix = nil,
|
|
example = [[
|
|
-- the next loop will probably crash the interpreter
|
|
repeat until load "for _ in _,_,_,_ do local function _() end"
|
|
]],
|
|
patch = [[
|
|
--- lparser.c 2016/05/13 19:10:16 2.153
|
|
+++ lparser.c 2016/06/17 19:52:48
|
|
@@ -323,6 +323,8 @@
|
|
luaK_nil(fs, reg, extra);
|
|
}
|
|
}
|
|
+ if (nexps > nvars)
|
|
+ ls->fs->freereg -= nexps - nvars; /* remove extra values */
|
|
}
|
|
|
|
|
|
@@ -1160,11 +1162,8 @@
|
|
int nexps;
|
|
checknext(ls, '=');
|
|
nexps = explist(ls, &e);
|
|
- if (nexps != nvars) {
|
|
+ if (nexps != nvars)
|
|
adjust_assign(ls, nvars, nexps, &e);
|
|
- if (nexps > nvars)
|
|
- ls->fs->freereg -= nexps - nvars; /* remove extra values */
|
|
- }
|
|
else {
|
|
luaK_setoneret(ls->fs, &e); /* close last expression */
|
|
luaK_storevar(ls->fs, &lh->v, &e);
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [[Checking a format for 'os.date' may read pass the format string]],
|
|
report = [[Nagaev Boris, 2016/07/10]],
|
|
since = [[5.3.3]],
|
|
fix = nil,
|
|
example = [[
|
|
This bug does not seem to happen with regular compilers.
|
|
It needs an "interceptor" 'memcmp' function that continues
|
|
reading memory after a difference is found.]],
|
|
patch = [[
|
|
2c2
|
|
< ** $Id: bugs,v 1.160 2018/05/24 20:25:14 roberto Exp roberto $
|
|
---
|
|
> ** $Id: bugs,v 1.160 2018/05/24 20:25:14 roberto Exp roberto $
|
|
263c263,264
|
|
< for (option = LUA_STRFTIMEOPTIONS; *option != '\0'; option += oplen) {
|
|
---
|
|
> int convlen = (int)strlen(conv);
|
|
> for (option = LUA_STRFTIMEOPTIONS; *option != '\0' && oplen <= convlen; option += oplen) {
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [[Lua can generate wrong code in functions with too many constants]],
|
|
report = [[Marco Schöpl, 2016/07/17]],
|
|
since = [[5.3.3]],
|
|
fix = nil,
|
|
example = [[See http://lua-users.org/lists/lua-l/2016-07/msg00303.html]],
|
|
patch = [[
|
|
--- lcode.c 2016/06/20 19:12:46 2.110
|
|
+++ lcode.c 2016/07/18 15:43:41
|
|
@@ -1018,8 +1018,8 @@
|
|
*/
|
|
static void codebinexpval (FuncState *fs, OpCode op,
|
|
expdesc *e1, expdesc *e2, int line) {
|
|
- int rk1 = luaK_exp2RK(fs, e1); /* both operands are "RK" */
|
|
- int rk2 = luaK_exp2RK(fs, e2);
|
|
+ int rk2 = luaK_exp2RK(fs, e2); /* both operands are "RK" */
|
|
+ int rk1 = luaK_exp2RK(fs, e1);
|
|
freeexps(fs, e1, e2);
|
|
e1->u.info = luaK_codeABC(fs, op, 0, rk1, rk2); /* generate opcode */
|
|
e1->k = VRELOCABLE; /* all those operations are relocatable */
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [[When a coroutine tries to resume a non-suspended coroutine,
|
|
it can do some mess (and break C assertions) before detecting the error]],
|
|
report = [[Marco Schöpl, 2016/07/20]],
|
|
since = [[ ]],
|
|
fix = nil,
|
|
example = [[
|
|
-- with C assertions on
|
|
A = coroutine.running()
|
|
B = coroutine.create(function() coroutine.resume(A) end)
|
|
coroutine.resume(B)
|
|
|
|
-- or
|
|
A = coroutine.wrap(function() pcall(A, _) end)
|
|
A()
|
|
]],
|
|
patch = [[
|
|
]]
|
|
}
|
|
|
|
|
|
-----------------------------------------------------------------
|
|
-- Lua 5.3.4
|
|
|
|
|
|
Bug{
|
|
what = [[Wrong code for a goto followed by a label inside an 'if']],
|
|
report = [[云风, 2017/04/13]],
|
|
since = [[5.2]],
|
|
fix = nil,
|
|
example = [[
|
|
-- should print 32323232..., but prints only '3'
|
|
if true then
|
|
goto LBL
|
|
::loop::
|
|
print(2)
|
|
::LBL::
|
|
print(3)
|
|
goto loop
|
|
end
|
|
]],
|
|
patch = [[
|
|
--- lparser.c 2017/04/19 17:20:42 2.155.1.1
|
|
+++ lparser.c 2017/04/29 18:11:40 2.155.1.2
|
|
@@ -1392,7 +1392,7 @@
|
|
luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */
|
|
enterblock(fs, &bl, 0); /* must enter block before 'goto' */
|
|
gotostat(ls, v.t); /* handle goto/break */
|
|
- skipnoopstat(ls); /* skip other no-op statements */
|
|
+ while (testnext(ls, ';')) {} /* skip semicolons */
|
|
if (block_follow(ls, 0)) { /* 'goto' is the entire block? */
|
|
leaveblock(fs);
|
|
return; /* and that is it */
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [[Lua crashes when building sequences with more than 2^30 elements.]],
|
|
report = [[Viacheslav Usov, 2017/05/11]],
|
|
since = [[ ]],
|
|
fix = nil,
|
|
example = [[
|
|
-- crashes if machine has enough memory
|
|
local t = {}
|
|
for i = 1, 0x7fffffff do
|
|
t[i] = i
|
|
end
|
|
]],
|
|
patch = [[
|
|
--- ltable.c 2017/04/19 17:20:42 2.118.1.1
|
|
+++ ltable.c 2018/05/24 18:34:38
|
|
@@ -223,7 +223,9 @@
|
|
unsigned int na = 0; /* number of elements to go to array part */
|
|
unsigned int optimal = 0; /* optimal size for array part */
|
|
/* loop while keys can fill more than half of total size */
|
|
- for (i = 0, twotoi = 1; *pna > twotoi / 2; i++, twotoi *= 2) {
|
|
+ for (i = 0, twotoi = 1;
|
|
+ twotoi > 0 && *pna > twotoi / 2;
|
|
+ i++, twotoi *= 2) {
|
|
if (nums[i] > 0) {
|
|
a += nums[i];
|
|
if (a > twotoi/2) { /* more than half elements present? */
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [[Table length computation overflows for sequences larger than
|
|
2^31 elements.]],
|
|
report = [[Viacheslav Usov, 2017/05/12]],
|
|
since = [[ ]],
|
|
fix = nil,
|
|
example = [[
|
|
-- on a machine with enough memory
|
|
local t = {}
|
|
for i = 1, 2147483681 do
|
|
t[i] = i
|
|
end
|
|
print(#t)
|
|
]],
|
|
patch = [[
|
|
--- ltable.h 2017/04/19 17:20:42 2.23.1.1
|
|
+++ ltable.h 2018/05/24 19:31:50
|
|
@@ -56,3 +56,3 @@
|
|
LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
|
|
-LUAI_FUNC int luaH_getn (Table *t);
|
|
+LUAI_FUNC lua_Unsigned luaH_getn (Table *t);
|
|
|
|
--- ltable.c 2018/05/24 19:22:37 2.118.1.2
|
|
+++ ltable.c 2018/05/24 19:25:05
|
|
@@ -614,4 +614,4 @@
|
|
|
|
-static int unbound_search (Table *t, unsigned int j) {
|
|
- unsigned int i = j; /* i is zero or a present index */
|
|
+static lua_Unsigned unbound_search (Table *t, lua_Unsigned j) {
|
|
+ lua_Unsigned i = j; /* i is zero or a present index */
|
|
j++;
|
|
@@ -620,3 +620,3 @@
|
|
i = j;
|
|
- if (j > cast(unsigned int, MAX_INT)/2) { /* overflow? */
|
|
+ if (j > l_castS2U(LUA_MAXINTEGER) / 2) { /* overflow? */
|
|
/* table was built with bad purposes: resort to linear search */
|
|
@@ -630,3 +630,3 @@
|
|
while (j - i > 1) {
|
|
- unsigned int m = (i+j)/2;
|
|
+ lua_Unsigned m = (i+j)/2;
|
|
if (ttisnil(luaH_getint(t, m))) j = m;
|
|
@@ -642,3 +642,3 @@
|
|
*/
|
|
-int luaH_getn (Table *t) {
|
|
+lua_Unsigned luaH_getn (Table *t) {
|
|
unsigned int j = t->sizearray;
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [[Lua does not check GC when creating error messages]],
|
|
report = [[Viacheslav Usov, 2017/07/06]],
|
|
since = [[5.3.2]],
|
|
fix = nil,
|
|
example = [[
|
|
function test()
|
|
bob.joe.larry = 23
|
|
end
|
|
|
|
-- memory will grow steadly
|
|
for i = 1, math.huge do
|
|
pcall(test)
|
|
if i % 100000 == 0 then
|
|
io.write(collectgarbage'count'*1024, "\n")
|
|
end
|
|
end
|
|
]],
|
|
patch = [[
|
|
--- ldebug.c 2017/04/19 17:20:42 2.121.1.1
|
|
+++ ldebug.c 2017/07/10 17:08:39
|
|
@@ -653,6 +653,7 @@
|
|
CallInfo *ci = L->ci;
|
|
const char *msg;
|
|
va_list argp;
|
|
+ luaC_checkGC(L); /* error message uses memory */
|
|
va_start(argp, fmt);
|
|
msg = luaO_pushvfstring(L, fmt, argp); /* format message */
|
|
va_end(argp);
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [[dead keys with nil values can stay in weak tables]],
|
|
report = [[云风 Cloud Wu, 2017/08/15]],
|
|
since = [[5.2]],
|
|
fix = nil,
|
|
example = [[
|
|
-- The following chunk, under a memory checker like valgrind,
|
|
-- produces a memory access violation.
|
|
|
|
local a = setmetatable({}, {__mode = 'kv'})
|
|
|
|
a['ABCDEFGHIJKLMNOPQRSTUVWXYZ' .. 'abcdefghijklmnopqrstuvwxyz'] = {}
|
|
a[next(a)] = nil
|
|
collectgarbage()
|
|
print(a['BCDEFGHIJKLMNOPQRSTUVWXYZ' .. 'abcdefghijklmnopqrstuvwxyz'])
|
|
]],
|
|
patch = [[
|
|
--- lgc.c 2016/12/22 13:08:50 2.215
|
|
+++ lgc.c 2017/08/31 16:08:23
|
|
@@ -643,8 +643,9 @@
|
|
for (n = gnode(h, 0); n < limit; n++) {
|
|
if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) {
|
|
setnilvalue(gval(n)); /* remove value ... */
|
|
- removeentry(n); /* and remove entry from table */
|
|
}
|
|
+ if (ttisnil(gval(n))) /* is entry empty? */
|
|
+ removeentry(n); /* remove entry from table */
|
|
}
|
|
}
|
|
}
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [['lua_pushcclosure' should not call the garbage collector when
|
|
'n' is zero.]],
|
|
report = [[Andrew Gierth, 2017/12/05]],
|
|
since = [[5.3.3]],
|
|
fix = nil,
|
|
example = [[ ]],
|
|
patch = [[
|
|
--- lapi.c 2017/04/19 17:13:00 2.259.1.1
|
|
+++ lapi.c 2017/12/06 18:14:45
|
|
@@ -533,6 +533,7 @@
|
|
lua_lock(L);
|
|
if (n == 0) {
|
|
setfvalue(L->top, fn);
|
|
+ api_incr_top(L);
|
|
}
|
|
else {
|
|
CClosure *cl;
|
|
@@ -546,9 +547,9 @@
|
|
/* does not need barrier because closure is white */
|
|
}
|
|
setclCvalue(L, L->top, cl);
|
|
+ api_incr_top(L);
|
|
+ luaC_checkGC(L);
|
|
}
|
|
- api_incr_top(L);
|
|
- luaC_checkGC(L);
|
|
lua_unlock(L);
|
|
}
|
|
]]
|
|
}
|
|
|
|
|
|
Bug{
|
|
what = [[memory-allocation error when resizing a table can leave it
|
|
in an inconsistent state.]],
|
|
report = [[Roberto, 2017/12/08]],
|
|
since = [[5.0]],
|
|
fix = nil,
|
|
example = [[
|
|
local a = {x = 1, y = 1, z = 1}
|
|
a[1] = 10 -- goes to the hash part (which has 4 slots)
|
|
print(a[1]) --> 10
|
|
|
|
-- assume that the 2nd memory allocation from now fails
|
|
pcall(rawset, a, 2, 20) -- forces a rehash
|
|
|
|
-- a[1] now exists both in the array part (because the array part
|
|
-- grew) and in the hash part (because the allocation of the hash
|
|
-- part failed, keeping it as it was).
|
|
-- This makes the following traversal goes forever...
|
|
for k,v in pairs(a) do print(k,v) end
|
|
]],
|
|
patch = [[
|
|
--- ltable.c 2018/05/24 19:39:05 2.118.1.3
|
|
+++ ltable.c 2018/06/04 16:00:25
|
|
@@ -332,17 +332,34 @@
|
|
}
|
|
|
|
|
|
+typedef struct {
|
|
+ Table *t;
|
|
+ unsigned int nhsize;
|
|
+} AuxsetnodeT;
|
|
+
|
|
+
|
|
+static void auxsetnode (lua_State *L, void *ud) {
|
|
+ AuxsetnodeT *asn = cast(AuxsetnodeT *, ud);
|
|
+ setnodevector(L, asn->t, asn->nhsize);
|
|
+}
|
|
+
|
|
+
|
|
void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
|
|
unsigned int nhsize) {
|
|
unsigned int i;
|
|
int j;
|
|
+ AuxsetnodeT asn;
|
|
unsigned int oldasize = t->sizearray;
|
|
int oldhsize = allocsizenode(t);
|
|
Node *nold = t->node; /* save old hash ... */
|
|
if (nasize > oldasize) /* array part must grow? */
|
|
setarrayvector(L, t, nasize);
|
|
/* create new hash part with appropriate size */
|
|
- setnodevector(L, t, nhsize);
|
|
+ asn.t = t; asn.nhsize = nhsize;
|
|
+ if (luaD_rawrunprotected(L, auxsetnode, &asn) != LUA_OK) { /* mem. error? */
|
|
+ setarrayvector(L, t, oldasize); /* array back to its original size */
|
|
+ luaD_throw(L, LUA_ERRMEM); /* rethrow memory error */
|
|
+ }
|
|
if (nasize < oldasize) { /* array part must shrink? */
|
|
t->sizearray = nasize;
|
|
/* re-insert elements from vanishing slice */
|
|
]]
|
|
}
|
|
|
|
|
|
|
|
--[=[
|
|
Bug{
|
|
what = [[Long brackets with a huge number of '=' overflow some
|
|
internal buffer arithmetic]],
|
|
report = [[Marco, 2018/12/12]],
|
|
since = [[5.1]],
|
|
fix = nil,
|
|
example = [[
|
|
local eqs = string.rep("=", 0x3ffffffe)
|
|
local code = "return [" .. eqs .. "[a]" .. eqs .. "]"
|
|
print(#assert(load(code))())
|
|
]],
|
|
patch = [[
|
|
]]
|
|
}
|
|
]=]
|
|
|
|
|
|
|
|
|
|
--[=[
|
|
Bug{
|
|
what = [[ ]],
|
|
report = [[ ]],
|
|
since = [[ ]],
|
|
fix = nil,
|
|
example = [[ ]],
|
|
patch = [[
|
|
]]
|
|
}
|
|
]=]
|
|
|
|
|