Bugs
Here is a list of all bugs found in each release of Lua since 4.0. Each entry includes a minimal example that exhibits the bug and a patch or solution, when available.
Every Lua release fixes all listed bugs in previous releases, except where noted. Nevertheless, some bugs found in recent releases actually exist since older releases.
If you want to report a bug, please read this first.
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:
@@ -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++)
Example:
function f() f() end f() -- it takes too long before a "stack overflow" error
Patch:
lgc.c:
@@ -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;
}
Example:
-- only with Lua compiled in debug mode
print({} .. 2)
Patch:
ldebug.c:
@@ -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");
}
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'.
Example:
print(string.find(string.rep("a", 2^20), string.rep(".?", 2^20)))
pcall may not restore previous error function when
inside coroutines.
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:
@@ -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? */
Example: See http://lua-users.org/lists/lua-l/2012-08/msg00149.html
Patch:
ldo.c:
@@ -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);
}
load/loadfile return wrong result when given an environment
for a binary chunk with no upvalues.
Example:
f = load(string.dump(function () return 1 end), nil, "b", {})
print(type(f)) --> table (should be a function)
Patch:
lbaselib.c:
@@ -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);
}
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:
ldblib.c:
@@ -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));
Example:
print(0xE+1)
Patch:
llex.c:
@@ -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? */
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);
nCcalls in coroutines.
Example:
coroutine.wrap(function() print(pcall(pcall,pcall,pcall,pcall,pcall,error,3)) end)()
Patch:
ldo.c:
@@ -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);
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.
Example: In C, set a line hook that simply yields, and then call any Lua function. You get an infinite loop of yields.
Example: This glitch happens when compiling Lua on some mingw systems; it does not seem to affect other systems.
Patch:
src/Makefile: @@ -50,3 +50,3 @@ $(LUA_A): $(CORE_O) $(LIB_O) - $(AR) $@ $(CORE_O) $(LIB_O) # DLL needs all object files + $(AR) $@ $(CORE_O) $(LIB_O) $(RANLIB) $@
Solution: To avoid running precompiled code from untrusted sources, raise an error if the first byte in the stream is the escape character (decimal 27).
Example:
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.
Example:
print(((1 or false) and true) or false) --> 1, but should be 'true'
Patch: (partial solution; see also 9)
lcode.c:
@@ -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;
luaV_settable may invalidate a reference to a table and try
to reuse it.
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:
@@ -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");
}
debug.getfenv does not check whether it has an argument.
Example:
debug.getfenv() -- should raise an error
Patch:
ldblib.c:
@@ -45,6 +45,7 @@
static int db_getfenv (lua_State *L) {
+ luaL_checkany(L, 1);
lua_getfenv(L, 1);
return 1;
}
Example: See this message.
Patch:
llex.c:
@@ -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;
}
string.format may get buffer as an argument when there are
missing arguments and format string is too long.
Example:
x = string.rep("x", 10000) .. "%d"
print(string.format(x)) -- gives wrong error message
Patch:
lstrlib.c:
@@ -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': {
io.read("*n","*n") may return garbage if second read fails.
Example:
print(io.read("*n", "*n")) --<< enter "10 hi"
--> file (0x884420) nil
Patch:
liolib.c:
@@ -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 */
+ }
}
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 (to be applied after the patch in 3):
lcode.c:
@@ -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;
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:
@@ -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;
}
Patch:
lparser.c: @@ -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 */ }
LUAI_MAXCSTACK must be smaller than -LUA_REGISTRYINDEX.
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
coroutine.resume pushes element without ensuring stack size.
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;
lua_checkstack may have arithmetic overflow for large 'size'.
Example:
print(unpack({1,2,3}, 0, 2^31-3))
Patch:
lapi.c:
@@ -93,15 +93,14 @@
LUA_API int lua_checkstack (lua_State *L, int size) {
- int res;
+ int res = 1;
lua_lock(L);
- if ((L->top - L->base + size) > LUAI_MAXCSTACK)
+ if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK)
res = 0; /* stack overflow */
- else {
+ else if (size > 0) {
luaD_checkstack(L, size);
if (L->ci->top < L->top + size)
L->ci->top = L->top + size;
- res = 1;
}
lua_unlock(L);
return res;
unpack with maximum indices may crash due to arithmetic overflow.
Example:
print(unpack({1,2,3}, 2^31-1, 2^31-1))
Patch:
lbaselib.c:
@@ -344,10 +344,12 @@
luaL_checktype(L, 1, LUA_TTABLE);
i = luaL_optint(L, 2, 1);
e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));
+ if (i > e) return 0; /* empty range */
n = e - i + 1; /* number of elements */
- if (n <= 0) return 0; /* empty range */
- luaL_checkstack(L, n, "table too big to unpack");
- for (; i<=e; i++) /* push arg[i...e] */
+ if (n <= 0 || !lua_checkstack(L, n)) /* n <= 0 means arith. overflow */
+ return luaL_error(L, "too many results to unpack");
+ lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */
+ while (i++ < e) /* push arg[i + 1...e] */
lua_rawgeti(L, 1, i);
return n;
}
Example:
a = string.dump(function()return;end) a = a:gsub(string.char(30,37,122,128), string.char(34,0,0), 1) loadstring(a)()
Patch:
ldebug.c:
@@ -275,12 +275,12 @@
static int precheck (const Proto *pt) {
check(pt->maxstacksize <= MAXSTACK);
- lua_assert(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);
- lua_assert(!(pt->is_vararg & VARARG_NEEDSARG) ||
+ check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);
+ check(!(pt->is_vararg & VARARG_NEEDSARG) ||
(pt->is_vararg & VARARG_HASARG));
check(pt->sizeupvalues <= pt->nups);
check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0);
- check(GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);
+ check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);
return 1;
}
@@ -363,7 +363,11 @@
}
switch (op) {
case OP_LOADBOOL: {
- check(c == 0 || pc+2 < pt->sizecode); /* check its jump */
+ if (c == 1) { /* does it jump? */
+ check(pc+2 < pt->sizecode); /* check its jump */
+ check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST ||
+ GETARG_C(pt->code[pc+1]) != 0);
+ }
break;
}
case OP_LOADNIL: {
@@ -428,7 +432,10 @@
}
case OP_SETLIST: {
if (b > 0) checkreg(pt, a + b);
- if (c == 0) pc++;
+ if (c == 0) {
+ pc++;
+ check(pc < pt->sizecode - 1);
+ }
break;
}
case OP_CLOSURE: {
Example:
function crash(depth)
local init = '\27\76\117\97\81\0\1\4\4\4\8\0\7\0\0\0\61\115\116' ..
'\100\105\110\0\1\0\0\0\1\0\0\0\0\0\0\2\2\0\0\0\36' ..
'\0\0\0\30\0\128\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0' ..
'\1\0\0\0\0\0\0\2'
local mid = '\1\0\0\0\30\0\128\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\0'
local fin = '\0\0\0\0\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2\0' ..
'\0\0\97\0\1\0\0\0\1\0\0\0\0\0\0\0'
local lch = '\2\0\0\0\36\0\0\0\30\0\128\0\0\0\0\0\1\0\0\0\0\0\0' ..
'\0\1\0\0\0\1\0\0\0\0\0\0\2'
local rch = '\0\0\0\0\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2\0' ..
'\0\0\97\0\1\0\0\0\1'
for i=1,depth do lch,rch = lch..lch,rch..rch end
loadstring(init .. lch .. mid .. rch .. fin)
end
for i=1,25 do print(i); crash(i) end
Patch:
lundump.c:
@@ -161,7 +160,9 @@
static Proto* LoadFunction(LoadState* S, TString* p)
{
- Proto* f=luaF_newproto(S->L);
+ Proto* f;
+ if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,"code too deep");
+ f=luaF_newproto(S->L);
setptvalue2s(S->L,S->L->top,f); incr_top(S->L);
f->source=LoadString(S); if (f->source==NULL) f->source=p;
f->linedefined=LoadInt(S);
@@ -175,6 +176,7 @@
LoadDebug(S,f);
IF (!luaG_checkcode(f), "bad code");
S->L->top--;
+ S->L->nCcalls--;
return f;
}
Example:
z={}
for i=1,27290 do z[i]='1,' end
z = 'if 1+1==2 then local a={' .. table.concat(z) .. '} end'
func = loadstring(z)
print(loadstring(string.dump(func)))
Patch:
ldebug.c:
@@ -346,9 +346,18 @@
int dest = pc+1+b;
check(0 <= dest && dest < pt->sizecode);
if (dest > 0) {
- /* cannot jump to a setlist count */
- Instruction d = pt->code[dest-1];
- check(!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0));
+ int j;
+ /* check that it does not jump to a setlist count; this
+ is tricky, because the count from a previous setlist may
+ have the same value of an invalid setlist; so, we must
+ go all the way back to the first of them (if any) */
+ for (j = 0; j < dest; j++) {
+ Instruction d = pt->code[dest-1-j];
+ if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break;
+ }
+ /* if 'j' is even, previous value is not a setlist (even if
+ it looks like one) */
+ check((j&1) == 0);
}
}
break;
Example:
maybe = string.dump(function() return ({[true]=true})[true] end)
maybe = maybe:gsub('\1\1','\1\2')
maybe = loadstring(maybe)()
assert(type(maybe) == "boolean" and maybe ~= true and maybe ~= false)
Patch:
lundump.c:
@@ -115,7 +115,7 @@
setnilvalue(o);
break;
case LUA_TBOOLEAN:
- setbvalue(o,LoadChar(S));
+ setbvalue(o,LoadChar(S)!=0);
break;
case LUA_TNUMBER:
setnvalue(o,LoadNumber(S));
string.byte gets confused with some out-of-range negative indices.
Example:
print(string.byte("abc", -5)) --> 97 98 99 (should print nothing)
Patch:
lstrlib.c:
@@ -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;
}
Example:
collectgarbage("setpause", 100) -- small value
collectgarbage("setstepmul", 2000) -- large value
collectgarbage("step",0)
Patch:
lapi.c:
@@ -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: {
module may change the environment of a C function.
Example:
pcall(module, "xuxu") assert(debug.getfenv(pcall) == xuxu)
Patch:
loadlib.c:
@@ -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);
svalue is wrong.
Example:
/* in luaconf.h */
#define LUAI_USER_ALIGNMENT_T union { char b[32]; }
Patch:
lobject.h: @@ -210,3 +210,3 @@ #define getstr(ts) cast(const char *, (ts) + 1) -#define svalue(o) getstr(tsvalue(o)) +#define svalue(o) getstr(rawtsvalue(o))
-nil, -true, and -false is wrong.
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;
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;
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'.)
Example:
a = nil; a = (1)..a
Patch:
ldebug.c:
@@ -565,8 +565,8 @@
void luaG_concaterror (lua_State *L, StkId p1, StkId p2) {
- if (ttisstring(p1)) p1 = p2;
- lua_assert(!ttisstring(p1));
+ if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;
+ lua_assert(!ttisstring(p1) && !ttisnumber(p1));
luaG_typeerror(L, p1, "concatenate");
}
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);
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 */
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));
}
gsub may go wild when wrongly called without its third
argument and with a large subject.
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);
table.remove removes last element of a table when given
an out-of-bound index.
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++) {
lua_setfenv may crash if called over an invalid object.
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;
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);
debug.sethook/gethook may overflow the thread's stack.
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));
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++;
}
Example:
local Var local function main() NoSuchName (function() Var=0 end) end main()The error message is attempt to call upvalue 'Var' (a nil value). It should be attempt to call global 'NoSuchName' (a nil value).
Patch:
ldebug.c:
@@ -435,14 +435,16 @@
break;
}
case OP_CLOSURE: {
- int nup;
+ int nup, j;
check(b < pt->sizep);
nup = pt->p[b]->nups;
check(pc + nup < pt->sizecode);
- for (; nup>0; nup--) {
- OpCode op1 = GET_OPCODE(pt->code[pc+nup]);
+ for (j = 1; j <= nup; j++) {
+ OpCode op1 = GET_OPCODE(pt->code[pc + j]);
check(op1 == OP_GETUPVAL || op1 == OP_MOVE);
}
+ if (reg != NO_REG) /* tracing? */
+ pc += nup; /* do not 'execute' these pseudo-instructions */
break;
}
case OP_VARARG: {
string.format("%") may read past the string.
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 */
os.date throws an error when result is the empty string.
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);
}
setfenv accepts invalid first argument.
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);
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;
+ }
}
}
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;
+ }
}
}
}
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;
Example: the bug has no effect on external behavior.
Patch: In loadlib.c, change all ocurrences of luaO_pushfstring to
lua_pushfstring.
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;
}
luaL_checkudata may produce wrong error message.
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 */
}
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
string.formatE 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;
luaL_dofile and luaL_dostring
should return all 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)))
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;
Example:
co = coroutine.create(function () coroutine.yield() end) debug.sethook(co, function() end, "lr") coroutine.resume(co) coroutine.resume(co)
Patch:
ldo.c:
@@ -389,6 +389,7 @@
return;
}
else { /* resuming from previous yield */
+ L->status = 0;
if (!f_isLua(ci)) { /* `common' yield? */
/* finish interrupted execution of `OP_CALL' */
lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL ||
@@ -399,7 +400,6 @@
else /* yielded inside a hook: just continue its execution */
L->base = L->ci->base;
}
- L->status = 0;
luaV_execute(L, cast_int(L->ci - L->base_ci));
}
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 */
lua_getupvalue and lua_setupvalue do not check for index too small.
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;
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;
rawset and rawget do not ignore extra arguments.
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);
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;
}
Example:
-- should print false, but prints nil print(not not (nil and 4))
io.popen) may crash Lua.
Example:
f = io.popen("ls")
f:close()
lua_closethread exists only in the manual.
Example:
function co_func (current_co) coroutine.resume(co) end co = coroutine.create(co_func) coroutine.resume(co) coroutine.resume(co) --> seg. fault
Patch:
ldo.c:
325,326c325
< if (nargs >= L->top - L->base)
< luaG_runerror(L, "cannot resume dead coroutine");
---
> lua_assert(nargs < L->top - L->base);
329c328,329
< else if (ci->state & CI_YIELD) { /* inside a yield? */
---
> else { /* inside a yield */
> lua_assert(ci->state & CI_YIELD);
344,345d343
< else
< luaG_runerror(L, "cannot resume non-suspended coroutine");
351a350,358
> static int resume_error (lua_State *L, const char *msg) {
> L->top = L->ci->base;
> setsvalue2s(L->top, luaS_new(L, msg));
> incr_top(L);
> lua_unlock(L);
> return LUA_ERRRUN;
> }
>
>
355a363,368
> if (L->ci == L->base_ci) {
> if (nargs >= L->top - L->base)
> return resume_error(L, "cannot resume dead coroutine");
> }
> else if (!(L->ci->state & CI_YIELD)) /* not inside a yield? */
> return resume_error(L, "cannot resume non-suspended coroutine");
file:close cannot be called without a file (results a crash).
Example:
> io.stdin.close() -- correct call should be io.stdin:close()
Patch:
liolib.c:
161c161
< if (lua_isnone(L, 1)) {
---
> if (lua_isnone(L, 1) && lua_type(L, lua_upvalueindex(1)) == LUA_TTABLE) {
Example:
Must recompile Lua with a change inlua.cand withlua_assertdefined: lua.c: 381a382 > lua_checkstack(l, 1000);
Patch:
lgc.c: 247c247 < if (!(ci->state & CI_C) && lim < ci->top) --- > if (lim < ci->top)
Example:
function g(x)
coroutine.yield(x)
end
function f (i)
debug.sethook(print, "l")
for j=1,1000 do
g(i+j)
end
end
co = coroutine.wrap(f)
co(10)
pcall(co)
pcall(co)
Patch:
lvm.c: 402d401 < L->ci->u.l.pc = &pc; 405a405 > L->ci->u.l.pc = &pc; 676,678c676 < lua_assert(ci->u.l.pc == &pc && < ttisfunction(ci->base - 1) && < (ci->state & CI_SAVEDPC)); --- > lua_assert(ttisfunction(ci->base - 1) && (ci->state & CI_SAVEDPC));
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);
tostring.
Example:
print{} -- on an AS400 machine
Patch:
liolib.c: 178c178 < char buff[32]; --- > char buff[128]; lbaselib.c: 327c327 < char buff[64]; --- > char buff[128];
local function does not increment stack size.
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;
Example:
Set your hooks as below.
It is weird to use a positive count without setting the count hook,
but it is not wrong.
lua_sethook(L, my_hook, LUA_MASKLINE | LUA_MASKRET, 1);
Patch:
lvm.c:
69c69
< if (mask > LUA_MASKLINE) { /* instruction-hook set? */
---
> if (mask & LUA_MASKCOUNT) { /* instruction-hook set? */
dofile eats one return value when called without arguments.
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_getupvalue and lua_setupvalue do not check for index too small.
rawset and rawget do not ignore extra arguments.
lua_pushuserdata(L,NULL) does not work.
ULONG_MAX>>10 may not fit into an int.