From 8b83417de982d068bd92e0428a42ca0cdd909789 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 7 Feb 2024 14:17:03 -0300 Subject: [PATCH 01/24] Avoids a warning when lua_Number is 'float' --- lmathlib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lmathlib.c b/lmathlib.c index f140d62330..4381063480 100644 --- a/lmathlib.c +++ b/lmathlib.c @@ -352,7 +352,7 @@ static lua_Number I2d (Rand64 x) { SRand64 sx = (SRand64)(trim64(x) >> shift64_FIG); lua_Number res = (lua_Number)(sx) * scaleFIG; if (sx < 0) - res += 1.0; /* correct the two's complement if negative */ + res += l_mathop(1.0); /* correct the two's complement if negative */ lua_assert(0 <= res && res < 1); return res; } From de794a6527058e75b674118b35f39dcbb13e88b1 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 26 Apr 2024 14:55:18 -0300 Subject: [PATCH 02/24] Towards release 5.4.7 --- lgc.c | 2 +- lua.h | 6 +++--- lvm.c | 2 +- manual/2html | 2 +- manual/manual.of | 10 +++++----- testes/pm.lua | 3 ++- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lgc.c b/lgc.c index 253a2892c9..5817f9eec3 100644 --- a/lgc.c +++ b/lgc.c @@ -1713,7 +1713,7 @@ static void fullinc (lua_State *L, global_State *g) { /* finish any pending sweep phase to start a new cycle */ luaC_runtilstate(L, bitmask(GCSpause)); luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ - g->gcstate = GCSenteratomic; /* go straight to atomic phase ??? */ + g->gcstate = GCSenteratomic; /* go straight to atomic phase */ luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ /* estimate must be correct after a full GC cycle */ lua_assert(g->GCestimate == gettotalbytes(g)); diff --git a/lua.h b/lua.h index 040cc8e43e..41a4bf8c78 100644 --- a/lua.h +++ b/lua.h @@ -13,13 +13,13 @@ #include -#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio" +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2024 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" #define LUA_VERSION_MAJOR_N 5 #define LUA_VERSION_MINOR_N 4 -#define LUA_VERSION_RELEASE_N 6 +#define LUA_VERSION_RELEASE_N 7 #define LUA_VERSION_NUM (LUA_VERSION_MAJOR_N * 100 + LUA_VERSION_MINOR_N) #define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + LUA_VERSION_RELEASE_N) @@ -507,7 +507,7 @@ struct lua_Debug { /****************************************************************************** -* Copyright (C) 1994-2023 Lua.org, PUC-Rio. +* Copyright (C) 1994-2024 Lua.org, PUC-Rio. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the diff --git a/lvm.c b/lvm.c index 918ae64ca4..fcd24e11dc 100644 --- a/lvm.c +++ b/lvm.c @@ -92,7 +92,7 @@ static int l_strton (const TValue *obj, TValue *result) { if (!cvt2num(obj)) /* is object not a string? */ return 0; else { - TString *st = tsvalue(obj); + TString *st = tsvalue(obj); return (luaO_str2num(getstr(st), result) == tsslen(st) + 1); } } diff --git a/manual/2html b/manual/2html index 43fd89133b..4a3e57715f 100755 --- a/manual/2html +++ b/manual/2html @@ -30,7 +30,7 @@ by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes

Copyright -© 2023 Lua.org, PUC-Rio. All rights reserved. +© 2024 Lua.org, PUC-Rio. All rights reserved.


diff --git a/manual/manual.of b/manual/manual.of index cef3e22a63..b68d41c929 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -289,7 +289,7 @@ Whenever there is an error, an @def{error object} is propagated with information about the error. Lua itself only generates errors whose error object is a string, -but programs may generate errors with +but programs can generate errors with any value as the error object. It is up to the Lua program or its host to handle such error objects. For historical reasons, @@ -298,7 +298,7 @@ even though it does not have to be a string. When you use @Lid{xpcall} (or @Lid{lua_pcall}, in C) -you may give a @def{message handler} +you can give a @def{message handler} to be called in case of errors. This function is called with the original error object and returns a new error object. @@ -343,7 +343,7 @@ which is then called a @def{metamethod}. In the previous example, the key is the string @St{__add} and the metamethod is the function that performs the addition. Unless stated otherwise, -a metamethod may in fact be any @x{callable value}, +a metamethod can in fact be any @x{callable value}, which is either a function or a value with a @idx{__call} metamethod. You can query the metatable of any value @@ -1417,7 +1417,7 @@ labels in Lua are considered statements too: A label is visible in the entire block where it is defined, except inside nested functions. -A goto may jump to any visible label as long as it does not +A goto can jump to any visible label as long as it does not enter into the scope of a local variable. A label should not be declared where a label with the same name is visible, @@ -4488,7 +4488,7 @@ but can contain other zeros in its body. This function can raise memory errors only when converting a number to a string -(as then it has to create a new string). +(as then it may create a new string). } diff --git a/testes/pm.lua b/testes/pm.lua index 44454dffa8..e5e3f7a77b 100644 --- a/testes/pm.lua +++ b/testes/pm.lua @@ -56,7 +56,8 @@ assert(f(" \n\r*&\n\r xuxu \n\n", "%g%g%g+") == "xuxu") -- Adapt a pattern to UTF-8 local function PU (p) - -- break '?' into each individual byte of a character + -- reapply '?' into each individual byte of a character. + -- (For instance, "รก?" becomes "\195?\161?".) p = string.gsub(p, "(" .. utf8.charpattern .. ")%?", function (c) return string.gsub(c, ".", "%0?") end) From dfbde4c7d540f81f2cc539741a2c1f4c00f91c10 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Mon, 13 May 2024 13:10:35 -0300 Subject: [PATCH 03/24] Bug: Active-lines for stripped vararg functions Lua seg. faults when asked to create the 'activelines' table for a vararg function with no debug information. --- ldebug.c | 36 +++++++++++++++++++----------------- manual/manual.of | 12 ++++++------ testes/db.lua | 9 +++++++++ 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/ldebug.c b/ldebug.c index d6f132ea2d..591b3528a7 100644 --- a/ldebug.c +++ b/ldebug.c @@ -31,7 +31,7 @@ -#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) +#define LuaClosure(f) ((f) != NULL && (f)->c.tt == LUA_VLCL) static const char *funcnamefromcall (lua_State *L, CallInfo *ci, @@ -254,7 +254,7 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { static void funcinfo (lua_Debug *ar, Closure *cl) { - if (noLuaClosure(cl)) { + if (!LuaClosure(cl)) { ar->source = "=[C]"; ar->srclen = LL("=[C]"); ar->linedefined = -1; @@ -288,29 +288,31 @@ static int nextline (const Proto *p, int currentline, int pc) { static void collectvalidlines (lua_State *L, Closure *f) { - if (noLuaClosure(f)) { + if (!LuaClosure(f)) { setnilvalue(s2v(L->top.p)); api_incr_top(L); } else { - int i; - TValue v; const Proto *p = f->l.p; int currentline = p->linedefined; Table *t = luaH_new(L); /* new table to store active lines */ sethvalue2s(L, L->top.p, t); /* push it on stack */ api_incr_top(L); - setbtvalue(&v); /* boolean 'true' to be the value of all indices */ - if (!p->is_vararg) /* regular function? */ - i = 0; /* consider all instructions */ - else { /* vararg function */ - lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); - currentline = nextline(p, currentline, 0); - i = 1; /* skip first instruction (OP_VARARGPREP) */ - } - for (; i < p->sizelineinfo; i++) { /* for each instruction */ - currentline = nextline(p, currentline, i); /* get its line */ - luaH_setint(L, t, currentline, &v); /* table[line] = true */ + if (p->lineinfo != NULL) { /* proto with debug information? */ + int i; + TValue v; + setbtvalue(&v); /* boolean 'true' to be the value of all indices */ + if (!p->is_vararg) /* regular function? */ + i = 0; /* consider all instructions */ + else { /* vararg function */ + lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); + currentline = nextline(p, currentline, 0); + i = 1; /* skip first instruction (OP_VARARGPREP) */ + } + for (; i < p->sizelineinfo; i++) { /* for each instruction */ + currentline = nextline(p, currentline, i); /* get its line */ + luaH_setint(L, t, currentline, &v); /* table[line] = true */ + } } } } @@ -339,7 +341,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, } case 'u': { ar->nups = (f == NULL) ? 0 : f->c.nupvalues; - if (noLuaClosure(f)) { + if (!LuaClosure(f)) { ar->isvararg = 1; ar->nparams = 0; } diff --git a/manual/manual.of b/manual/manual.of index b68d41c929..c5c7469696 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -8773,13 +8773,13 @@ The returned table can contain all the fields returned by @Lid{lua_getinfo}, with the string @id{what} describing which fields to fill in. The default for @id{what} is to get all information available, except the table of valid lines. -If present, -the option @Char{f} +The option @Char{f} adds a field named @id{func} with the function itself. -If present, -the option @Char{L} -adds a field named @id{activelines} with the table of -valid lines. +The option @Char{L} adds a field named @id{activelines} +with the table of valid lines, +provided the function is a Lua function. +If the function has no debug information, +the table is empty. For instance, the expression @T{debug.getinfo(1,"n").name} returns a name for the current function, diff --git a/testes/db.lua b/testes/db.lua index d3758c4151..49ff8e3e89 100644 --- a/testes/db.lua +++ b/testes/db.lua @@ -49,6 +49,15 @@ do end +-- bug in 5.4.4-5.4.6: activelines in vararg functions +-- without debug information +do + local func = load(string.dump(load("print(10)"), true)) + local actl = debug.getinfo(func, "L").activelines + assert(#actl == 0) -- no line info +end + + -- test file and string names truncation local a = "function f () end" local function dostring (s, x) return load(s, x)() end From e84f7bf19852c35ad0a1e9a1654a7b99a211e17c Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Mon, 13 May 2024 13:23:45 -0300 Subject: [PATCH 04/24] Details Typos in comments. --- lua.c | 4 ++-- luaconf.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lua.c b/lua.c index 3af5ce6a7f..af1f6a3e17 100644 --- a/lua.c +++ b/lua.c @@ -211,7 +211,7 @@ static int dostring (lua_State *L, const char *s, const char *name) { /* ** Receives 'globname[=modname]' and runs 'globname = require(modname)'. ** If there is no explicit modname and globname contains a '-', cut -** the sufix after '-' (the "version") to make the global name. +** the suffix after '-' (the "version") to make the global name. */ static int dolibrary (lua_State *L, char *globname) { int status; @@ -230,7 +230,7 @@ static int dolibrary (lua_State *L, char *globname) { status = docall(L, 1, 1); /* call 'require(modname)' */ if (status == LUA_OK) { if (suffix != NULL) /* is there a suffix mark? */ - *suffix = '\0'; /* remove sufix from global name */ + *suffix = '\0'; /* remove suffix from global name */ lua_setglobal(L, globname); /* globname = require(modname) */ } return report(L, status); diff --git a/luaconf.h b/luaconf.h index acebe29c99..33bb580d17 100644 --- a/luaconf.h +++ b/luaconf.h @@ -261,7 +261,7 @@ /* ** LUA_IGMARK is a mark to ignore all after it when building the ** module name (e.g., used to build the luaopen_ function name). -** Typically, the sufix after the mark is the module version, +** Typically, the suffix after the mark is the module version, ** as in "mod-v1.2.so". */ #define LUA_IGMARK "-" From e0efebdbe4e4053c6fb78588c546f1dc23aa964a Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 14 May 2024 17:59:54 -0300 Subject: [PATCH 05/24] Detail in the manual Function 'lua_toclose' can raise a non-memory error (but not a memory error). --- manual/manual.of | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/manual/manual.of b/manual/manual.of index c5c7469696..da71fbe972 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -4416,7 +4416,7 @@ otherwise, returns @id{NULL}. } @APIEntry{void lua_toclose (lua_State *L, int index);| -@apii{0,0,m} +@apii{0,0,v} Marks the given index in the stack as a to-be-closed slot @see{to-be-closed}. @@ -4433,6 +4433,9 @@ A slot marked as to-be-closed should not be removed from the stack by any other function in the API except @Lid{lua_settop} or @Lid{lua_pop}, unless previously deactivated by @Lid{lua_closeslot}. +This function raises an error if the value at the given slot +neither has a @idx{__close} metamethod nor is a false value. + This function should not be called for an index that is equal to or below an active to-be-closed slot. From d5212c13b081ed62d8e1ae436779e79c79edf564 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 4 Jun 2024 12:48:29 -0300 Subject: [PATCH 06/24] More disciplined use of 'errno' Set errno to zero before calling any function where we may use its errno, and check errno for zero before using it (as functions may not set it even in error). --- lauxlib.c | 18 +++++++++++++----- liolib.c | 31 +++++++++++++++++++++++-------- loslib.c | 2 ++ 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/lauxlib.c b/lauxlib.c index 1c9082e67e..28bff274c7 100644 --- a/lauxlib.c +++ b/lauxlib.c @@ -249,11 +249,13 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { return 1; } else { + const char *msg; luaL_pushfail(L); + msg = (en != 0) ? strerror(en) : "(no extra info)"; if (fname) - lua_pushfstring(L, "%s: %s", fname, strerror(en)); + lua_pushfstring(L, "%s: %s", fname, msg); else - lua_pushstring(L, strerror(en)); + lua_pushstring(L, msg); lua_pushinteger(L, en); return 3; } @@ -732,9 +734,12 @@ static const char *getF (lua_State *L, void *ud, size_t *size) { static int errfile (lua_State *L, const char *what, int fnameindex) { - const char *serr = strerror(errno); + int err = errno; const char *filename = lua_tostring(L, fnameindex) + 1; - lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + if (err != 0) + lua_pushfstring(L, "cannot %s %s: %s", what, filename, strerror(err)); + else + lua_pushfstring(L, "cannot %s %s", what, filename); lua_remove(L, fnameindex); return LUA_ERRFILE; } @@ -787,6 +792,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, } else { lua_pushfstring(L, "@%s", filename); + errno = 0; lf.f = fopen(filename, "r"); if (lf.f == NULL) return errfile(L, "open", fnameindex); } @@ -796,6 +802,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, if (c == LUA_SIGNATURE[0]) { /* binary file? */ lf.n = 0; /* remove possible newline */ if (filename) { /* "real" file? */ + errno = 0; lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ if (lf.f == NULL) return errfile(L, "reopen", fnameindex); skipcomment(lf.f, &c); /* re-read initial portion */ @@ -805,6 +812,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); readstatus = ferror(lf.f); + errno = 0; /* no useful error number until here */ if (filename) fclose(lf.f); /* close file (even in case of errors) */ if (readstatus) { lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ @@ -933,7 +941,7 @@ LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { luaL_checkstack(L, nup, "too many upvalues"); for (; l->name != NULL; l++) { /* fill the table with given functions */ - if (l->func == NULL) /* place holder? */ + if (l->func == NULL) /* placeholder? */ lua_pushboolean(L, 0); else { int i; diff --git a/liolib.c b/liolib.c index b08397da45..82f444b0e1 100644 --- a/liolib.c +++ b/liolib.c @@ -245,8 +245,8 @@ static int f_gc (lua_State *L) { */ static int io_fclose (lua_State *L) { LStream *p = tolstream(L); - int res = fclose(p->f); - return luaL_fileresult(L, (res == 0), NULL); + errno = 0; + return luaL_fileresult(L, (fclose(p->f) == 0), NULL); } @@ -272,6 +272,7 @@ static int io_open (lua_State *L) { LStream *p = newfile(L); const char *md = mode; /* to traverse/check mode */ luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); + errno = 0; p->f = fopen(filename, mode); return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; } @@ -292,6 +293,7 @@ static int io_popen (lua_State *L) { const char *mode = luaL_optstring(L, 2, "r"); LStream *p = newprefile(L); luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode"); + errno = 0; p->f = l_popen(L, filename, mode); p->closef = &io_pclose; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; @@ -300,6 +302,7 @@ static int io_popen (lua_State *L) { static int io_tmpfile (lua_State *L) { LStream *p = newfile(L); + errno = 0; p->f = tmpfile(); return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; } @@ -603,8 +606,10 @@ static int g_read (lua_State *L, FILE *f, int first) { } } } - if (ferror(f)) + if (ferror(f)) { + errno = 0; /* no relevant errno here */ return luaL_fileresult(L, 0, NULL); + } if (!success) { lua_pop(L, 1); /* remove last result */ luaL_pushfail(L); /* push nil instead */ @@ -678,7 +683,10 @@ static int g_write (lua_State *L, FILE *f, int arg) { } if (l_likely(status)) return 1; /* file handle already on stack top */ - else return luaL_fileresult(L, status, NULL); + else { + errno = 0; /* no relevant errno here */ + return luaL_fileresult(L, status, NULL); + } } @@ -703,6 +711,7 @@ static int f_seek (lua_State *L) { l_seeknum offset = (l_seeknum)p3; luaL_argcheck(L, (lua_Integer)offset == p3, 3, "not an integer in proper range"); + errno = 0; op = l_fseek(f, offset, mode[op]); if (l_unlikely(op)) return luaL_fileresult(L, 0, NULL); /* error */ @@ -719,19 +728,25 @@ static int f_setvbuf (lua_State *L) { FILE *f = tofile(L); int op = luaL_checkoption(L, 2, NULL, modenames); lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); - int res = setvbuf(f, NULL, mode[op], (size_t)sz); + int res; + errno = 0; + res = setvbuf(f, NULL, mode[op], (size_t)sz); return luaL_fileresult(L, res == 0, NULL); } static int io_flush (lua_State *L) { - return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); + FILE *f = getiofile(L, IO_OUTPUT); + errno = 0; + return luaL_fileresult(L, fflush(f) == 0, NULL); } static int f_flush (lua_State *L) { - return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); + FILE *f = tofile(L); + errno = 0; + return luaL_fileresult(L, fflush(f) == 0, NULL); } @@ -773,7 +788,7 @@ static const luaL_Reg meth[] = { ** metamethods for file handles */ static const luaL_Reg metameth[] = { - {"__index", NULL}, /* place holder */ + {"__index", NULL}, /* placeholder */ {"__gc", f_gc}, {"__close", f_gc}, {"__tostring", f_tostring}, diff --git a/loslib.c b/loslib.c index ad5a927688..ba80d72c45 100644 --- a/loslib.c +++ b/loslib.c @@ -155,6 +155,7 @@ static int os_execute (lua_State *L) { static int os_remove (lua_State *L) { const char *filename = luaL_checkstring(L, 1); + errno = 0; return luaL_fileresult(L, remove(filename) == 0, filename); } @@ -162,6 +163,7 @@ static int os_remove (lua_State *L) { static int os_rename (lua_State *L) { const char *fromname = luaL_checkstring(L, 1); const char *toname = luaL_checkstring(L, 2); + errno = 0; return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); } From ae9a0cbbb446499e759acae47664d1d136d7ba90 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 4 Jun 2024 12:52:52 -0300 Subject: [PATCH 07/24] Bug: overlapping assignments ISO C forbids assignment of a union field to another field of the same union. --- lcode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lcode.c b/lcode.c index caac6ba322..87616140eb 100644 --- a/lcode.c +++ b/lcode.c @@ -776,7 +776,8 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) { break; } case VLOCAL: { /* already in a register */ - e->u.info = e->u.var.ridx; + int temp = e->u.var.ridx; + e->u.info = temp; /* (can't do a direct assignment; values overlap) */ e->k = VNONRELOC; /* becomes a non-relocatable value */ break; } @@ -1283,8 +1284,9 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ luaK_exp2anyreg(fs, t); /* put it in a register */ if (t->k == VUPVAL) { + int temp = t->u.info; /* upvalue index */ lua_assert(isKstr(fs, k)); - t->u.ind.t = t->u.info; /* upvalue index */ + t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */ t->u.ind.idx = k->u.info; /* literal short string */ t->k = VINDEXUP; } From 2db966fcbf757775c842bc66449d7e697826aa1d Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 4 Jun 2024 16:40:53 -0300 Subject: [PATCH 08/24] Bug: luaL_traceback may need more than 5 stack slots --- lauxlib.c | 1 + ltests.c | 5 +++++ testes/errors.lua | 15 ++++++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lauxlib.c b/lauxlib.c index 28bff274c7..baa67ce644 100644 --- a/lauxlib.c +++ b/lauxlib.c @@ -80,6 +80,7 @@ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) { int top = lua_gettop(L); lua_getinfo(L, "f", ar); /* push function */ lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + luaL_checkstack(L, 6, "not enough stack"); /* slots for 'findfield' */ if (findfield(L, top + 1, 2)) { const char *name = lua_tostring(L, -1); if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */ diff --git a/ltests.c b/ltests.c index c2943a4f8e..a27cdb0796 100644 --- a/ltests.c +++ b/ltests.c @@ -1650,6 +1650,11 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { int nres; status = lua_resume(lua_tothread(L1, i), L, getnum, &nres); } + else if EQ("traceback") { + const char *msg = getstring; + int level = getnum; + luaL_traceback(L1, L1, msg, level); + } else if EQ("return") { int n = getnum; if (L1 != L) { diff --git a/testes/errors.lua b/testes/errors.lua index 01cfe9060c..80d91a9213 100644 --- a/testes/errors.lua +++ b/testes/errors.lua @@ -91,7 +91,7 @@ end if not T then (Message or print) - ('\n >>> testC not active: skipping memory message test <<<\n') + ('\n >>> testC not active: skipping tests for messages in C <<<\n') else print "testing memory error message" local a = {} @@ -104,6 +104,19 @@ else end) T.totalmem(0) assert(not st and msg == "not enough" .. " memory") + + -- stack space for luaL_traceback (bug in 5.4.6) + local res = T.testC[[ + # push 16 elements on the stack + pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1; + pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1; + pushnum 1; pushnum 1; pushnum 1; pushnum 1; pushnum 1; + pushnum 1; + # traceback should work with 4 remaining slots + traceback xuxu 1; + return 1 + ]] + assert(string.find(res, "xuxu.-main chunk")) end From 7eb1ed21b7057ab5f1b921f8271eddcf13659737 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 5 Jun 2024 11:50:42 -0300 Subject: [PATCH 09/24] More permissive use of 'errno' Assume that no function will put garbage on errno (although ISO C allows that). If any function during an operation set errno, and the operation result in an error, assume that errno has something to say. --- lauxlib.c | 2 +- liolib.c | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lauxlib.c b/lauxlib.c index baa67ce644..923105ed31 100644 --- a/lauxlib.c +++ b/lauxlib.c @@ -811,9 +811,9 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, } if (c != EOF) lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ + errno = 0; status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); readstatus = ferror(lf.f); - errno = 0; /* no useful error number until here */ if (filename) fclose(lf.f); /* close file (even in case of errors) */ if (readstatus) { lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ diff --git a/liolib.c b/liolib.c index 82f444b0e1..c5075f3e78 100644 --- a/liolib.c +++ b/liolib.c @@ -570,6 +570,7 @@ static int g_read (lua_State *L, FILE *f, int first) { int nargs = lua_gettop(L) - 1; int n, success; clearerr(f); + errno = 0; if (nargs == 0) { /* no arguments? */ success = read_line(L, f, 1); n = first + 1; /* to return 1 result */ @@ -606,10 +607,8 @@ static int g_read (lua_State *L, FILE *f, int first) { } } } - if (ferror(f)) { - errno = 0; /* no relevant errno here */ + if (ferror(f)) return luaL_fileresult(L, 0, NULL); - } if (!success) { lua_pop(L, 1); /* remove last result */ luaL_pushfail(L); /* push nil instead */ @@ -665,6 +664,7 @@ static int io_readline (lua_State *L) { static int g_write (lua_State *L, FILE *f, int arg) { int nargs = lua_gettop(L) - arg; int status = 1; + errno = 0; for (; nargs--; arg++) { if (lua_type(L, arg) == LUA_TNUMBER) { /* optimization: could be done exactly as for strings */ @@ -683,10 +683,8 @@ static int g_write (lua_State *L, FILE *f, int arg) { } if (l_likely(status)) return 1; /* file handle already on stack top */ - else { - errno = 0; /* no relevant errno here */ + else return luaL_fileresult(L, status, NULL); - } } From 21ff8de33a5aca9c3c907592b894e4b9ab036d3e Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 5 Jun 2024 13:37:16 -0300 Subject: [PATCH 10/24] Bug: Tricky _PROMPT may trigger undefined behavior --- lua.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lua.c b/lua.c index af1f6a3e17..6da331f118 100644 --- a/lua.c +++ b/lua.c @@ -115,12 +115,13 @@ static void l_message (const char *pname, const char *msg) { /* ** Check whether 'status' is not OK and, if so, prints the error -** message on the top of the stack. It assumes that the error object -** is a string, as it was either generated by Lua or by 'msghandler'. +** message on the top of the stack. */ static int report (lua_State *L, int status) { if (status != LUA_OK) { const char *msg = lua_tostring(L, -1); + if (msg == NULL) + msg = "(error message not a string)"; l_message(progname, msg); lua_pop(L, 1); /* remove message */ } From 1ab3208a1fceb12fca8f24ba57d6e13c5bff15e3 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 13 Jun 2024 15:13:52 -0300 Subject: [PATCH 11/24] 'lua.h' back to redundancy in version definitions Several tools inspect 'lua.h' to extract version information, and they assume the file will have some specific format. --- lua.h | 32 +++++++++++--------------------- lundump.h | 2 +- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/lua.h b/lua.h index 41a4bf8c78..f050dac091 100644 --- a/lua.h +++ b/lua.h @@ -1,7 +1,7 @@ /* ** $Id: lua.h $ ** Lua - A Scripting Language -** Lua.org, PUC-Rio, Brazil (www.lua.org) +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** See Copyright Notice at the end of this file */ @@ -13,19 +13,20 @@ #include -#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2024 Lua.org, PUC-Rio" -#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" - +#include "luaconf.h" -#define LUA_VERSION_MAJOR_N 5 -#define LUA_VERSION_MINOR_N 4 -#define LUA_VERSION_RELEASE_N 7 -#define LUA_VERSION_NUM (LUA_VERSION_MAJOR_N * 100 + LUA_VERSION_MINOR_N) -#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + LUA_VERSION_RELEASE_N) +#define LUA_VERSION_MAJOR "5" +#define LUA_VERSION_MINOR "4" +#define LUA_VERSION_RELEASE "7" +#define LUA_VERSION_NUM 504 +#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 7) -#include "luaconf.h" +#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2024 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" /* mark for precompiled code ('Lua') */ @@ -495,17 +496,6 @@ struct lua_Debug { /* }====================================================================== */ -#define LUAI_TOSTRAUX(x) #x -#define LUAI_TOSTR(x) LUAI_TOSTRAUX(x) - -#define LUA_VERSION_MAJOR LUAI_TOSTR(LUA_VERSION_MAJOR_N) -#define LUA_VERSION_MINOR LUAI_TOSTR(LUA_VERSION_MINOR_N) -#define LUA_VERSION_RELEASE LUAI_TOSTR(LUA_VERSION_RELEASE_N) - -#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR -#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE - - /****************************************************************************** * Copyright (C) 1994-2024 Lua.org, PUC-Rio. * diff --git a/lundump.h b/lundump.h index bc71ced842..a97676ca18 100644 --- a/lundump.h +++ b/lundump.h @@ -21,7 +21,7 @@ /* ** Encode major-minor version in one byte, one nibble for each */ -#define LUAC_VERSION (LUA_VERSION_MAJOR_N*16+LUA_VERSION_MINOR_N) +#define LUAC_VERSION (((LUA_VERSION_NUM / 100) * 16) + LUA_VERSION_NUM % 100) #define LUAC_FORMAT 0 /* this is the official format */ From 30982bec968fd34694b5be3ecbbc92de78d8eacb Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 5 Jul 2024 14:31:07 -0300 Subject: [PATCH 12/24] Bug: Bad stack manipulation in 'multiline' (REPL) 'incomplete' was popping error message that should be used in case there is no more lines to complete the input, that is, 'pushline' returns NULL, due to end of file. --- lua.c | 11 +++++------ testes/main.lua | 5 +++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lua.c b/lua.c index 6da331f118..4a90e55dd9 100644 --- a/lua.c +++ b/lua.c @@ -490,10 +490,8 @@ static int incomplete (lua_State *L, int status) { if (status == LUA_ERRSYNTAX) { size_t lmsg; const char *msg = lua_tolstring(L, -1, &lmsg); - if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) { - lua_pop(L, 1); + if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) return 1; - } } return 0; /* else... */ } @@ -508,9 +506,9 @@ static int pushline (lua_State *L, int firstline) { size_t l; const char *prmt = get_prompt(L, firstline); int readstatus = lua_readline(L, b, prmt); - if (readstatus == 0) - return 0; /* no input (prompt will be popped by caller) */ lua_pop(L, 1); /* remove prompt */ + if (readstatus == 0) + return 0; /* no input */ l = strlen(b); if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ b[--l] = '\0'; /* remove it */ @@ -552,8 +550,9 @@ static int multiline (lua_State *L) { int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ if (!incomplete(L, status) || !pushline(L, 0)) { lua_saveline(L, line); /* keep history */ - return status; /* cannot or should not try to add continuation line */ + return status; /* should not or cannot try to add continuation line */ } + lua_remove(L, -2); /* remove error message (from incomplete line) */ lua_pushliteral(L, "\n"); /* add newline... */ lua_insert(L, -2); /* ...between the two lines */ lua_concat(L, 3); /* join them */ diff --git a/testes/main.lua b/testes/main.lua index 11b14b4464..cec4fa04fb 100644 --- a/testes/main.lua +++ b/testes/main.lua @@ -349,6 +349,11 @@ prepfile("a = [[b\nc\nd\ne]]\n=a") RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) checkprogout("b\nc\nd\ne\n\n") +-- input interrupted in continuation line +prepfile("a.\n") +RUN([[lua -i < %s > /dev/null 2> %s]], prog, out) +checkprogout("near \n") + local prompt = "alo" prepfile[[ -- a = 2 From 782ef85b22f89d1cd1ab083202f018668d26e4b0 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Sat, 17 Aug 2024 12:37:04 -0300 Subject: [PATCH 13/24] Bug: wrong code gen. for indices with comparisons In function 'luaK_exp2val', used to generate code for indices: Macro 'hasjumps' does not consider the case when the whole expression is a "jump" (a test). In all other of its uses, the surrounding code ensures that the expression cannot be VJMP. --- lcode.c | 3 ++- testes/closure.lua | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lcode.c b/lcode.c index 87616140eb..3f78370c6e 100644 --- a/lcode.c +++ b/lcode.c @@ -35,6 +35,7 @@ #define MAXREGS 255 +/* (note that expressions VJMP also have jumps.) */ #define hasjumps(e) ((e)->t != (e)->f) @@ -985,7 +986,7 @@ void luaK_exp2anyregup (FuncState *fs, expdesc *e) { ** or it is a constant. */ void luaK_exp2val (FuncState *fs, expdesc *e) { - if (hasjumps(e)) + if (e->k == VJMP || hasjumps(e)) luaK_exp2anyreg(fs, e); else luaK_dischargevars(fs, e); diff --git a/testes/closure.lua b/testes/closure.lua index ea038e8245..37271472a6 100644 --- a/testes/closure.lua +++ b/testes/closure.lua @@ -3,6 +3,14 @@ print "testing closures" +do -- bug in 5.4.7 + _ENV[true] = 10 + local function aux () return _ENV[1 < 2] end + assert(aux() == 10) + _ENV[true] = nil +end + + local A,B = 0,{g=10} local function f(x) local a = {} From 9f0c0fe0de64bc41efea73a2da881ff0a1036a03 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 10 Sep 2024 11:34:42 -0300 Subject: [PATCH 14/24] Bug: Wrong limit for local variables in 16-bit systems USHRT_MAX does not fit in an 'int' in 16-bit systems. --- lapi.c | 2 +- lparser.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lapi.c b/lapi.c index 332e97d169..04e09cff7e 100644 --- a/lapi.c +++ b/lapi.c @@ -1343,7 +1343,7 @@ void lua_warning (lua_State *L, const char *msg, int tocont) { LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { Udata *u; lua_lock(L); - api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value"); + api_check(L, 0 <= nuvalue && nuvalue < SHRT_MAX, "invalid value"); u = luaS_newudata(L, size, nuvalue); setuvalue(L, s2v(L->top.p), u); api_incr_top(L); diff --git a/lparser.c b/lparser.c index 2b888c7cff..aebddaf70b 100644 --- a/lparser.c +++ b/lparser.c @@ -198,7 +198,7 @@ static int new_localvar (LexState *ls, TString *name) { checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, MAXVARS, "local variables"); luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, - dyd->actvar.size, Vardesc, USHRT_MAX, "local variables"); + dyd->actvar.size, Vardesc, SHRT_MAX, "local variables"); var = &dyd->actvar.arr[dyd->actvar.n++]; var->vd.kind = VDKREG; /* default */ var->vd.name = name; From f5e55be2a0ce64066c1b0554675633b92c91fafb Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 16 Jan 2025 16:25:11 -0300 Subject: [PATCH 15/24] Bug: Missing error status in panic function 'luaD_throw' may call 'luaE_resetthread', which returns an error code but clears 'L->status'; so, 'luaD_throw' should set that status again. --- ldo.c | 1 + ltests.c | 3 +++ testes/api.lua | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/ldo.c b/ldo.c index ea0529507e..958b1b7d67 100644 --- a/ldo.c +++ b/ldo.c @@ -120,6 +120,7 @@ l_noret luaD_throw (lua_State *L, int errcode) { else { /* thread has no error handler */ global_State *g = G(L); errcode = luaE_resetthread(L, errcode); /* close all upvalues */ + L->status = errcode; if (g->mainthread->errorJmp) { /* main thread has a handler? */ setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */ luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ diff --git a/ltests.c b/ltests.c index a27cdb0796..af0f43e226 100644 --- a/ltests.c +++ b/ltests.c @@ -1655,6 +1655,9 @@ static int runC (lua_State *L, lua_State *L1, const char *pc) { int level = getnum; luaL_traceback(L1, L1, msg, level); } + else if EQ("threadstatus") { + lua_pushstring(L1, statcodes[lua_status(L1)]); + } else if EQ("return") { int n = getnum; if (L1 != L) { diff --git a/testes/api.lua b/testes/api.lua index 752ff18ff3..eab3059b2a 100644 --- a/testes/api.lua +++ b/testes/api.lua @@ -399,6 +399,10 @@ do -- trivial error assert(T.checkpanic("pushstring hi; error") == "hi") + -- thread status inside panic (bug in 5.4.4) + assert(T.checkpanic("pushstring hi; error", "threadstatus; return 2") == + "ERRRUN") + -- using the stack inside panic assert(T.checkpanic("pushstring hi; error;", [[checkstack 5 XX From 25da574fcbb68bf507431a6091ab73ac434c9428 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 12 Mar 2025 16:01:03 -0300 Subject: [PATCH 16/24] Bug: 'luaD_seterrorobj' should not raise errors This function can be called unprotected, so it should not raise any kind of errors. (It could raise a memory-allocation error when creating a message). --- ldo.c | 16 +++++++++++----- ldo.h | 1 + lstate.c | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ldo.c b/ldo.c index 958b1b7d67..c92573d6e6 100644 --- a/ldo.c +++ b/ldo.c @@ -94,10 +94,6 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ break; } - case LUA_ERRERR: { - setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); - break; - } case LUA_OK: { /* special case only for closing upvalues */ setnilvalue(s2v(oldtop)); /* no error message */ break; @@ -199,6 +195,16 @@ static void correctstack (lua_State *L) { /* some space for error handling */ #define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) + +/* raise an error while running the message handler */ +l_noret luaD_errerr (lua_State *L) { + TString *msg = luaS_newliteral(L, "error in error handling"); + setsvalue2s(L, L->top.p, msg); + L->top.p++; /* assume EXTRA_STACK */ + luaD_throw(L, LUA_ERRERR); +} + + /* ** Reallocate the stack to a new size, correcting all pointers into it. ** In ISO C, any pointer use after the pointer has been deallocated is @@ -248,7 +254,7 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) { a stack error; cannot grow further than that. */ lua_assert(stacksize(L) == ERRORSTACKSIZE); if (raiseerror) - luaD_throw(L, LUA_ERRERR); /* error inside message handler */ + luaD_errerr(L); /* error inside message handler */ return 0; /* if not 'raiseerror', just signal it */ } else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */ diff --git a/ldo.h b/ldo.h index 56008ab30f..4de9540ec8 100644 --- a/ldo.h +++ b/ldo.h @@ -60,6 +60,7 @@ /* type of protected functions, to be ran by 'runprotected' */ typedef void (*Pfunc) (lua_State *L, void *ud); +LUAI_FUNC l_noret luaD_errerr (lua_State *L); LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, const char *mode); diff --git a/lstate.c b/lstate.c index 7fefacba2c..89c8b6ad0e 100644 --- a/lstate.c +++ b/lstate.c @@ -166,7 +166,7 @@ void luaE_checkcstack (lua_State *L) { if (getCcalls(L) == LUAI_MAXCCALLS) luaG_runerror(L, "C stack overflow"); else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) - luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ + luaD_errerr(L); /* error while handling stack error */ } From 983bc433e6a60cbc4fe3a16f1d4713bacb8e3509 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 13 Mar 2025 15:42:39 -0300 Subject: [PATCH 17/24] Bug: Use after free in 'luaV_finishset' If a metatable is a weak table, its __newindex field could be collected by an emergency collection while being used in 'luaV_finishset'. (This bug has similarities with bug 5.3.2-1, fixed in commit a272fa66.) --- lvm.c | 3 +++ testes/events.lua | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/lvm.c b/lvm.c index fcd24e11dc..7023a04da8 100644 --- a/lvm.c +++ b/lvm.c @@ -339,7 +339,10 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, lua_assert(isempty(slot)); /* slot must be empty */ tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ if (tm == NULL) { /* no metamethod? */ + sethvalue2s(L, L->top.p, h); /* anchor 't' */ + L->top.p++; /* assume EXTRA_STACK */ luaH_finishset(L, h, key, slot, val); /* set new value */ + L->top.p--; invalidateTMcache(h); luaC_barrierback(L, obj2gco(h), val); return; diff --git a/testes/events.lua b/testes/events.lua index 8d8563b952..def13dc8fc 100644 --- a/testes/events.lua +++ b/testes/events.lua @@ -370,6 +370,19 @@ x = 0 .."a".."b"..c..d.."e".."f".."g" assert(x.val == "0abcdefg") +do + -- bug since 5.4.1 + local mt = setmetatable({__newindex={}}, {__mode='v'}) + local t = setmetatable({}, mt) + + if T then T.allocfailnext() end + + -- seg. fault + for i=1, 10 do t[i] = 1 end +end + + + -- concat metamethod x numbers (bug in 5.1.1) c = {} local x From 3fe7be956f23385aa1950dc31e2f25127ccfc0ea Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Mon, 17 Mar 2025 16:14:17 -0300 Subject: [PATCH 18/24] Bug: message handler can be overwritten A __close metamethod can overwrite a message handler in the stack when closing a thread or a state. --- lstate.c | 3 +++ testes/coroutine.lua | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/lstate.c b/lstate.c index 89c8b6ad0e..f3f2ccfdd5 100644 --- a/lstate.c +++ b/lstate.c @@ -272,7 +272,9 @@ static void close_state (lua_State *L) { luaC_freeallobjects(L); /* just collect its objects */ else { /* closing a fully built state */ L->ci = &L->base_ci; /* unwind CallInfo list */ + L->errfunc = 0; /* stack unwind can "throw away" the error function */ luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ + L->top.p = L->stack.p + 1; /* empty the stack to run finalizers */ luaC_freeallobjects(L); /* collect all objects */ luai_userstateclose(L); } @@ -328,6 +330,7 @@ int luaE_resetthread (lua_State *L, int status) { if (status == LUA_YIELD) status = LUA_OK; L->status = LUA_OK; /* so it can run __close metamethods */ + L->errfunc = 0; /* stack unwind can "throw away" the error function */ status = luaD_closeprotected(L, 1, status); if (status != LUA_OK) /* errors? */ luaD_seterrorobj(L, status, L->stack.p + 1); diff --git a/testes/coroutine.lua b/testes/coroutine.lua index e566c86e96..03e044513e 100644 --- a/testes/coroutine.lua +++ b/testes/coroutine.lua @@ -493,6 +493,25 @@ assert(not pcall(a, a)) a = nil +do + -- bug in 5.4: thread can use message handler higher in the stack + -- than the variable being closed + local c = coroutine.create(function() + local clo = setmetatable({}, {__close=function() + local x = 134 -- will overwrite message handler + error(x) + end}) + -- yields coroutine but leaves a new message handler for it, + -- that would be used when closing the coroutine (except that it + -- will be overwritten) + xpcall(coroutine.yield, function() return "XXX" end) + end) + + assert(coroutine.resume(c)) -- start coroutine + local st, msg = coroutine.close(c) + assert(not st and msg == 134) +end + -- access to locals of erroneous coroutines local x = coroutine.create (function () local a = 10 From d1ee2a4deb06a335b9ff99530917b828a7ebe3e5 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 15 May 2025 15:14:56 -0300 Subject: [PATCH 19/24] Bug: Bad error message with fields of string _ENV Errors with fields of the string _ENV (e.g., ("_ENV").u + 1) report the error as comming from a global. --- ldebug.c | 21 +++++++++++++++------ testes/errors.lua | 4 ++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ldebug.c b/ldebug.c index 591b3528a7..7264fce8a5 100644 --- a/ldebug.c +++ b/ldebug.c @@ -37,6 +37,9 @@ static const char *funcnamefromcall (lua_State *L, CallInfo *ci, const char **name); +static const char strlocal[] = "local"; +static const char strupval[] = "upvalue"; + static int currentpc (CallInfo *ci) { lua_assert(isLua(ci)); @@ -497,7 +500,7 @@ static const char *basicgetobjname (const Proto *p, int *ppc, int reg, int pc = *ppc; *name = luaF_getlocalname(p, reg + 1, pc); if (*name) /* is a local? */ - return "local"; + return strlocal; /* else try symbolic execution */ *ppc = pc = findsetreg(p, pc, reg); if (pc != -1) { /* could find instruction? */ @@ -512,7 +515,7 @@ static const char *basicgetobjname (const Proto *p, int *ppc, int reg, } case OP_GETUPVAL: { *name = upvalname(p, GETARG_B(i)); - return "upvalue"; + return strupval; } case OP_LOADK: return kname(p, GETARG_Bx(i), name); case OP_LOADKX: return kname(p, GETARG_Ax(p->code[pc + 1]), name); @@ -547,15 +550,21 @@ static void rkname (const Proto *p, int pc, Instruction i, const char **name) { /* ** Check whether table being indexed by instruction 'i' is the -** environment '_ENV' +** environment '_ENV'. If the table is an upvalue, get its name; +** otherwise, find some "name" for the table and check whether +** that name is the name of a local variable (and not, for instance, +** a string). Then check that, if there is a name, it is '_ENV'. */ static const char *isEnv (const Proto *p, int pc, Instruction i, int isup) { int t = GETARG_B(i); /* table index */ const char *name; /* name of indexed variable */ if (isup) /* is 't' an upvalue? */ name = upvalname(p, t); - else /* 't' is a register */ - basicgetobjname(p, &pc, t, &name); + else { /* 't' is a register */ + const char *what = basicgetobjname(p, &pc, t, &name); + if (what != strlocal && what != strupval) + name = NULL; /* cannot be the variable _ENV */ + } return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field"; } @@ -701,7 +710,7 @@ static const char *getupvalname (CallInfo *ci, const TValue *o, for (i = 0; i < c->nupvalues; i++) { if (c->upvals[i]->v.p == o) { *name = upvalname(c->p, i); - return "upvalue"; + return strupval; } } return NULL; diff --git a/testes/errors.lua b/testes/errors.lua index 80d91a9213..15401b4f22 100644 --- a/testes/errors.lua +++ b/testes/errors.lua @@ -137,6 +137,10 @@ checkmessage("aaa=(1)..{}", "a table value") -- bug in 5.4.6 checkmessage("a = {_ENV = {}}; print(a._ENV.x + 1)", "field 'x'") +-- a similar bug in 5.4.7, since 5.4.0 +checkmessage("print(('_ENV').x + 1)", "field 'x'") + + _G.aaa, _G.bbbb = nil -- calls From 267ef461d0b13aa28d667f12d1a61452c9db529c Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 20 May 2025 17:55:08 -0300 Subject: [PATCH 20/24] Bug: check for constructor overflow in [exp] fields The check for constructor overflow was considering only fields with explicit names, ignoring fields with syntax '[exp]=exp'. --- lparser.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lparser.c b/lparser.c index aebddaf70b..1ac82990e0 100644 --- a/lparser.c +++ b/lparser.c @@ -849,12 +849,11 @@ static void recfield (LexState *ls, ConsControl *cc) { FuncState *fs = ls->fs; int reg = ls->fs->freereg; expdesc tab, key, val; - if (ls->t.token == TK_NAME) { - checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); + if (ls->t.token == TK_NAME) codename(ls, &key); - } else /* ls->t.token == '[' */ yindex(ls, &key); + checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); cc->nh++; checknext(ls, '='); tab = *cc->t; From 6e22fedb74cf0c9b6656e9fce8b7331db847c605 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 21 May 2025 13:52:56 -0300 Subject: [PATCH 21/24] New release number, updated copyright year --- lua.h | 8 ++++---- manual/2html | 2 +- testes/all.lua | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lua.h b/lua.h index f050dac091..f3ea590d9c 100644 --- a/lua.h +++ b/lua.h @@ -18,14 +18,14 @@ #define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MINOR "4" -#define LUA_VERSION_RELEASE "7" +#define LUA_VERSION_RELEASE "8" #define LUA_VERSION_NUM 504 -#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 7) +#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 8) #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE -#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2024 Lua.org, PUC-Rio" +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2025 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" @@ -497,7 +497,7 @@ struct lua_Debug { /****************************************************************************** -* Copyright (C) 1994-2024 Lua.org, PUC-Rio. +* Copyright (C) 1994-2025 Lua.org, PUC-Rio. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the diff --git a/manual/2html b/manual/2html index 4a3e57715f..1588c1e673 100755 --- a/manual/2html +++ b/manual/2html @@ -30,7 +30,7 @@ by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes

Copyright -© 2024 Lua.org, PUC-Rio. All rights reserved. +© 2025 Lua.org, PUC-Rio. All rights reserved.


diff --git a/testes/all.lua b/testes/all.lua index 5df0ff9bca..413d4da2ab 100644 --- a/testes/all.lua +++ b/testes/all.lua @@ -287,7 +287,7 @@ print("final OK !!!") --[[ ***************************************************************************** -* Copyright (C) 1994-2016 Lua.org, PUC-Rio. +* Copyright (C) 1994-2025 Lua.org, PUC-Rio. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the From 1b0f943da7dfb25987456a77259edbeea0b94edc Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Mon, 16 Jun 2025 16:33:02 -0300 Subject: [PATCH 22/24] Bug: new metatable in weak table can fool the GC All-weak tables are not being revisited after being visited during propagation; if it gets a new metatable after that, the new metatable may not be marked. --- lgc.c | 8 ++++++-- testes/gc.lua | 10 ++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lgc.c b/lgc.c index 5817f9eec3..c01660abc5 100644 --- a/lgc.c +++ b/lgc.c @@ -553,8 +553,12 @@ static lu_mem traversetable (global_State *g, Table *h) { traverseweakvalue(g, h); else if (!weakvalue) /* strong values? */ traverseephemeron(g, h, 0); - else /* all weak */ - linkgclist(h, g->allweak); /* nothing to traverse now */ + else { /* all weak */ + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must visit again its metatable */ + else + linkgclist(h, g->allweak); /* must clear collected entries */ + } } else /* not weak */ traversestrongtable(g, h); diff --git a/testes/gc.lua b/testes/gc.lua index 03093e34ff..f017f33056 100644 --- a/testes/gc.lua +++ b/testes/gc.lua @@ -301,6 +301,16 @@ collectgarbage() assert(next(a) == string.rep('$', 11)) +if T then -- bug since 5.3: all-weak tables are not being revisited + T.gcstate("propagate") + local t = setmetatable({}, {__mode = "kv"}) + T.gcstate("atomic") -- 't' was visited + setmetatable(t, {__mode = "kv"}) + T.gcstate("pause") -- its new metatable is not being visited + assert(getmetatable(t).__mode == "kv") +end + + -- 'bug' in 5.1 a = {} local t = {x = 10} From 9ac9d23f4164fc7e1eeea8a3e8d4e453dade51ab Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 27 Aug 2025 14:55:35 -0300 Subject: [PATCH 23/24] Bug: error with option '--' without a script --- lua.c | 3 ++- testes/main.lua | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lua.c b/lua.c index 4a90e55dd9..8055b70fe9 100644 --- a/lua.c +++ b/lua.c @@ -302,7 +302,8 @@ static int collectargs (char **argv, int *first) { case '-': /* '--' */ if (argv[i][2] != '\0') /* extra characters after '--'? */ return has_error; /* invalid option */ - *first = i + 1; + /* if there is a script name, it comes after '--' */ + *first = (argv[i + 1] != NULL) ? i + 1 : 0; return args; case '\0': /* '-' */ return args; /* script "name" is '-' */ diff --git a/testes/main.lua b/testes/main.lua index cec4fa04fb..ff408f139f 100644 --- a/testes/main.lua +++ b/testes/main.lua @@ -90,7 +90,7 @@ prepfile[[ 1, a ) ]] -RUN('lua - < %s > %s', prog, out) +RUN('lua - -- < %s > %s', prog, out) checkout("1\tnil\n") RUN('echo "print(10)\nprint(2)\n" | lua > %s', out) @@ -133,7 +133,7 @@ checkout("-h\n") prepfile("print(package.path)") -- test LUA_PATH -RUN('env LUA_INIT= LUA_PATH=x lua %s > %s', prog, out) +RUN('env LUA_INIT= LUA_PATH=x lua -- %s > %s', prog, out) checkout("x\n") -- test LUA_PATH_version @@ -346,7 +346,7 @@ RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) checkprogout("6\n10\n10\n\n") prepfile("a = [[b\nc\nd\ne]]\n=a") -RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out) +RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i -- < %s > %s]], prog, out) checkprogout("b\nc\nd\ne\n\n") -- input interrupted in continuation line @@ -478,12 +478,14 @@ assert(not os.remove(out)) -- invalid options NoRun("unrecognized option '-h'", "lua -h") NoRun("unrecognized option '---'", "lua ---") -NoRun("unrecognized option '-Ex'", "lua -Ex") +NoRun("unrecognized option '-Ex'", "lua -Ex --") NoRun("unrecognized option '-vv'", "lua -vv") NoRun("unrecognized option '-iv'", "lua -iv") NoRun("'-e' needs argument", "lua -e") NoRun("syntax error", "lua -e a") NoRun("'-l' needs argument", "lua -l") +NoRun("-i", "lua -- -i") -- handles -i as a script name + if T then -- test library? From 934fdd481ced3a9d4a7aaace4479ce889ab23582 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 27 Aug 2025 14:58:02 -0300 Subject: [PATCH 24/24] Bug: Constructors with nils can overflow counters --- lparser.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lparser.c b/lparser.c index 1ac82990e0..f4bfc96346 100644 --- a/lparser.c +++ b/lparser.c @@ -940,6 +940,8 @@ static void constructor (LexState *ls, expdesc *t) { if (ls->t.token == '}') break; closelistfield(fs, &cc); field(ls, &cc); + checklimit(fs, cc.tostore + cc.na + cc.nh, INT_MAX/2, + "items in a constructor"); } while (testnext(ls, ',') || testnext(ls, ';')); check_match(ls, '}', '{', line); lastlistfield(fs, &cc);