diff --git a/lua54.changes b/lua54.changes index c013210..eab8782 100644 --- a/lua54.changes +++ b/lua54.changes @@ -1,3 +1,19 @@ +------------------------------------------------------------------- +Thu Dec 30 18:34:01 UTC 2021 - Callum Farmer + +- Re-enable readline support in Lua, the way to do this changed + in Lua 5.4 +- Because we are linking with readline add GPLv3+ only to the + main package +- Subsequently, update main_test.patch to ignore another test + +------------------------------------------------------------------- +Thu Dec 30 13:41:04 UTC 2021 - Callum Farmer + +- Update upstream-bugs.patch and upstream-bugs-test.patch to fix + bugs 9,10,12 for build and tests respectively. Bug 11 changes + interface of luaD_pretailcall. + ------------------------------------------------------------------- Sat Nov 27 16:54:20 UTC 2021 - Callum Farmer diff --git a/lua54.spec b/lua54.spec index cf1ce5e..4b595be 100644 --- a/lua54.spec +++ b/lua54.spec @@ -29,7 +29,7 @@ Name: lua54%{name_ext} Version: 5.4.3 Release: 0 Summary: Small Embeddable Language with Procedural Syntax -License: MIT +License: GPL-3.0-or-later Group: Development/Languages/Other URL: http://www.lua.org Source: http://www.lua.org/ftp/lua-%{version}.tar.gz @@ -74,6 +74,7 @@ of C functions, written in ANSI C. %package devel Summary: Development files for lua +License: MIT Group: Development/Libraries/C and C++ Requires: %{libname} = %{version} Requires: %{name} = %{version} @@ -94,6 +95,7 @@ application. %package -n %{libname} Summary: The Lua integration library +License: MIT Group: System/Libraries # Compat as libtool changes the soname %ifarch aarch64 x86_64 ppc64 ppc64le s390x riscv64 @@ -120,6 +122,7 @@ of C functions, written in ANSI C. %package doc Summary: Documentation for Lua, a small embeddable language +License: MIT Group: Documentation/HTML BuildArch: noarch Supplements: (lua54 and patterns-base-documentation) @@ -157,17 +160,14 @@ cat doc/luac.1 | sed 's/TH LUAC 1/TH LUAC%{major_version} 1/' > doc/luac%{major_ %build sed -i -e "s@lib/lua/@%{_lib}/lua/@g" src/luaconf.h -make %{_smp_mflags} VERBOSE=1 -C src \ +make linux-readline %{_smp_mflags} VERBOSE=1 -C src \ CC="cc" \ - MYCFLAGS="%{optflags} -std=gnu99 -D_GNU_SOURCE -fPIC -DLUA_USE_LINUX -DLUA_COMPAT_MODULE" \ - MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses" \ + MYCFLAGS="%{optflags} -std=gnu99 -D_GNU_SOURCE -fPIC -DLUA_COMPAT_MODULE" \ V=%{major_version} \ - LIBTOOL="libtool --quiet" \ - all + LIBTOOL="libtool --quiet" %install %make_install \ - V=%{major_version} \ LIBTOOL="libtool --quiet" \ INSTALL_TOP="%{buildroot}%{_prefix}" \ INSTALL_LIB="%{buildroot}%{_libdir}" diff --git a/main_test.patch b/main_test.patch index 0037896..89f08a4 100644 --- a/main_test.patch +++ b/main_test.patch @@ -1,5 +1,14 @@ --- a/main.lua +++ b/main.lua +@@ -47,7 +47,7 @@ + assert(string.sub(s, -1) == "\n") + local t = getoutput() + for line in string.gmatch(s, ".-\n") do +- assert(string.find(t, line, 1, true)) ++ -- assert(string.find(t, line, 1, true)) + end + end + @@ -307,11 +307,11 @@ ]] RUN([[lua -e "%s" -i < %s > %s]], prompt, prog, out) diff --git a/upstream-bugs-test.patch b/upstream-bugs-test.patch index 614cc6e..c344157 100644 --- a/upstream-bugs-test.patch +++ b/upstream-bugs-test.patch @@ -287,3 +287,84 @@ index 461e03770..76c9d6e63 100644 -- attempt to resume 'normal' coroutine local co1, co2 co1 = coroutine.create(function () return co2() end) +diff --git a/testes/api.lua b/testes/api.lua +index c1bcb4b7b..bd85a923c 100644 +--- a/api.lua ++++ b/api.lua +@@ -804,15 +804,14 @@ F = function (x) + d = nil + assert(debug.getmetatable(x).__gc == F) + assert(load("table.insert({}, {})"))() -- create more garbage +- collectgarbage() -- force a GC during GC +- assert(debug.getmetatable(x).__gc == F) -- previous GC did not mess this? ++ assert(not collectgarbage()) -- GC during GC (no op) + local dummy = {} -- create more garbage during GC + if A ~= nil then + assert(type(A) == "userdata") + assert(T.udataval(A) == B) + debug.getmetatable(A) -- just access it + end +- A = x -- ressucita userdata ++ A = x -- ressurect userdata + B = udval + return 1,2,3 + end +diff --git a/testes/gc.lua b/testes/gc.lua +index 2332c939a..d865cb28d 100644 +--- a/gc.lua ++++ b/gc.lua +@@ -676,11 +676,13 @@ end + -- just to make sure + assert(collectgarbage'isrunning') + +-do -- check that the collector is reentrant in incremental mode ++do -- check that the collector is not reentrant in incremental mode ++ local res = true + setmetatable({}, {__gc = function () +- collectgarbage() ++ res = collectgarbage() + end}) + collectgarbage() ++ assert(not res) + end + + +diff --git a/testes/main.lua b/testes/main.lua +index 52c779541..9def63860 100644 +--- a/main.lua ++++ b/main.lua +@@ -261,6 +261,34 @@ u2 = setmetatable({}, {__gc = function () error("ZYX") end}) + RUN('lua -W %s 2> %s', prog, out) + checkprogout("ZYX)\nXYZ)\n") + ++-- bug since 5.2: finalizer called when closing a state could ++-- subvert finalization order ++prepfile[[ ++-- should be called last ++print("creating 1") ++setmetatable({}, {__gc = function () print(1) end}) ++ ++print("creating 2") ++setmetatable({}, {__gc = function () ++ print("2") ++ print("creating 3") ++ -- this finalizer should not be called, as object will be ++ -- created after 'lua_close' has been called ++ setmetatable({}, {__gc = function () print(3) end}) ++ print(collectgarbage()) -- cannot call collector here ++ os.exit(0, true) ++end}) ++]] ++RUN('lua -W %s > %s', prog, out) ++checkout[[ ++creating 1 ++creating 2 ++2 ++creating 3 ++nil ++1 ++]] ++ + + -- test many arguments + prepfile[[print(({...})[30])]] diff --git a/upstream-bugs.patch b/upstream-bugs.patch index fdc398d..76db31b 100644 --- a/upstream-bugs.patch +++ b/upstream-bugs.patch @@ -325,3 +325,341 @@ index bfc590262..5cb0847c8 100644 luaD_reallocstack(L, cast_int(ci->top - L->stack), 0); return status; } +From 1de95e97ef65632a88e08b6184bd9d1ceba7ec2f Mon Sep 17 00:00:00 2001 +From: Roberto Ierusalimschy +Date: Fri, 10 Dec 2021 10:53:54 -0300 +Subject: [PATCH] Bug: Lua stack still active when closing a state + +--- + lstate.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/lstate.c b/lstate.c +index 5cb0847c8..547a7a014 100644 +--- a/src/lstate.c ++++ b/src/lstate.c +@@ -271,6 +271,7 @@ static void close_state (lua_State *L) { + if (!completestate(g)) /* closing a partially built state? */ + luaC_freeallobjects(L); /* jucst collect its objects */ + else { /* closing a fully built state */ ++ L->ci = &L->base_ci; /* unwind CallInfo list */ + luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ + luaC_freeallobjects(L); /* collect all objects */ + luai_userstateclose(L); +From 0bfc572e51d9035a615ef6e9523f736c9ffa8e57 Mon Sep 17 00:00:00 2001 +From: Roberto Ierusalimschy +Date: Mon, 13 Dec 2021 10:41:17 -0300 +Subject: [PATCH] Bug: GC is not reentrant + +As the GC is not reentrant, finalizers should not be able to invoke it. +--- + lapi.c | 17 +++++++++-------- + lbaselib.c | 19 +++++++++++++++++-- + lgc.c | 11 +++++++---- + lgc.h | 9 +++++++++ + lstate.c | 4 ++-- + lstate.h | 2 +- + manual/manual.of | 11 ++++++----- + testes/api.lua | 5 ++--- + testes/gc.lua | 6 ++++-- + 9 files changed, 57 insertions(+), 27 deletions(-) + +diff --git a/lapi.c b/lapi.c +index 071a06f3d..3585ac436 100644 +--- a/src/lapi.c ++++ b/src/lapi.c +@@ -1136,18 +1136,19 @@ LUA_API int lua_status (lua_State *L) { + LUA_API int lua_gc (lua_State *L, int what, ...) { + va_list argp; + int res = 0; +- global_State *g; ++ global_State *g = G(L); ++ if (g->gcstp & GCSTPGC) /* internal stop? */ ++ return -1; /* all options are invalid when stopped */ + lua_lock(L); +- g = G(L); + va_start(argp, what); + switch (what) { + case LUA_GCSTOP: { +- g->gcrunning = 0; ++ g->gcstp = GCSTPUSR; /* stopeed by the user */ + break; + } + case LUA_GCRESTART: { + luaE_setdebt(g, 0); +- g->gcrunning = 1; ++ g->gcstp = 0; /* (GCSTPGC must be already zero here) */ + break; + } + case LUA_GCCOLLECT: { +@@ -1166,8 +1167,8 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { + case LUA_GCSTEP: { + int data = va_arg(argp, int); + l_mem debt = 1; /* =1 to signal that it did an actual step */ +- lu_byte oldrunning = g->gcrunning; +- g->gcrunning = 1; /* allow GC to run */ ++ lu_byte oldstp = g->gcstp; ++ g->gcstp = 0; /* allow GC to run (GCSTPGC must be zero here) */ + if (data == 0) { + luaE_setdebt(g, 0); /* do a basic step */ + luaC_step(L); +@@ -1177,7 +1178,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { + luaE_setdebt(g, debt); + luaC_checkGC(L); + } +- g->gcrunning = oldrunning; /* restore previous state */ ++ g->gcstp = oldstp; /* restore previous state */ + if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */ + res = 1; /* signal it */ + break; +@@ -1195,7 +1196,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { + break; + } + case LUA_GCISRUNNING: { +- res = g->gcrunning; ++ res = gcrunning(g); + break; + } + case LUA_GCGEN: { +diff --git a/lbaselib.c b/lbaselib.c +index 912c4cc63..1d60c9ded 100644 +--- a/src/lbaselib.c ++++ b/src/lbaselib.c +@@ -182,12 +182,20 @@ static int luaB_rawset (lua_State *L) { + + + static int pushmode (lua_State *L, int oldmode) { +- lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" +- : "generational"); ++ if (oldmode == -1) ++ luaL_pushfail(L); /* invalid call to 'lua_gc' */ ++ else ++ lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" ++ : "generational"); + return 1; + } + + ++/* ++** check whether call to 'lua_gc' was valid (not inside a finalizer) ++*/ ++#define checkvalres(res) { if (res == -1) break; } ++ + static int luaB_collectgarbage (lua_State *L) { + static const char *const opts[] = {"stop", "restart", "collect", + "count", "step", "setpause", "setstepmul", +@@ -200,12 +208,14 @@ static int luaB_collectgarbage (lua_State *L) { + case LUA_GCCOUNT: { + int k = lua_gc(L, o); + int b = lua_gc(L, LUA_GCCOUNTB); ++ checkvalres(k); + lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024)); + return 1; + } + case LUA_GCSTEP: { + int step = (int)luaL_optinteger(L, 2, 0); + int res = lua_gc(L, o, step); ++ checkvalres(res); + lua_pushboolean(L, res); + return 1; + } +@@ -213,11 +223,13 @@ static int luaB_collectgarbage (lua_State *L) { + case LUA_GCSETSTEPMUL: { + int p = (int)luaL_optinteger(L, 2, 0); + int previous = lua_gc(L, o, p); ++ checkvalres(previous); + lua_pushinteger(L, previous); + return 1; + } + case LUA_GCISRUNNING: { + int res = lua_gc(L, o); ++ checkvalres(res); + lua_pushboolean(L, res); + return 1; + } +@@ -234,10 +246,13 @@ static int luaB_collectgarbage (lua_State *L) { + } + default: { + int res = lua_gc(L, o); ++ checkvalres(res); + lua_pushinteger(L, res); + return 1; + } + } ++ luaL_pushfail(L); /* invalid call (inside a finalizer) */ ++ return 1; + } + + +diff --git a/lgc.c b/lgc.c +index b360eed00..7d0b5e4f7 100644 +--- a/src/lgc.c ++++ b/src/lgc.c +@@ -906,16 +906,16 @@ static void GCTM (lua_State *L) { + if (!notm(tm)) { /* is there a finalizer? */ + int status; + lu_byte oldah = L->allowhook; +- int running = g->gcrunning; ++ int oldgcstp = g->gcstp; ++ g->gcstp = GCSTPGC; /* avoid GC steps */ + L->allowhook = 0; /* stop debug hooks during GC metamethod */ +- g->gcrunning = 0; /* avoid GC steps */ + setobj2s(L, L->top++, tm); /* push finalizer... */ + setobj2s(L, L->top++, &v); /* ... and its argument */ + L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ + status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); + L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ + L->allowhook = oldah; /* restore hooks */ +- g->gcrunning = running; /* restore state */ ++ g->gcstp = oldgcstp; /* restore state */ + if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */ + luaE_warnerror(L, "__gc metamethod"); + L->top--; /* pops error object */ +@@ -1502,9 +1502,11 @@ static void deletelist (lua_State *L, GCObject *p, GCObject *limit) { + */ + void luaC_freeallobjects (lua_State *L) { + global_State *g = G(L); ++ g->gcstp = GCSTPGC; + luaC_changemode(L, KGC_INC); + separatetobefnz(g, 1); /* separate all objects with finalizers */ + lua_assert(g->finobj == NULL); ++ g->gcstp = 0; + callallpendingfinalizers(L); + deletelist(L, g->allgc, obj2gco(g->mainthread)); + deletelist(L, g->finobj, NULL); +@@ -1647,6 +1649,7 @@ void luaC_runtilstate (lua_State *L, int statesmask) { + } + + ++ + /* + ** Performs a basic incremental step. The debt and step size are + ** converted from bytes to "units of work"; then the function loops +@@ -1678,7 +1681,7 @@ static void incstep (lua_State *L, global_State *g) { + void luaC_step (lua_State *L) { + global_State *g = G(L); + lua_assert(!g->gcemergency); +- if (g->gcrunning) { /* running? */ ++ if (gcrunning(g)) { /* running? */ + if(isdecGCmodegen(g)) + genstep(L, g); + else +diff --git a/lgc.h b/lgc.h +index 073e2a402..024a4328e 100644 +--- a/src/lgc.h ++++ b/src/lgc.h +@@ -148,6 +148,15 @@ + */ + #define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0) + ++ ++/* ++** Control when GC is running: ++*/ ++#define GCSTPUSR 1 /* bit true when GC stopped by user */ ++#define GCSTPGC 2 /* bit true when GC stopped by itself */ ++#define gcrunning(g) ((g)->gcstp == 0) ++ ++ + /* + ** Does one step of collection when debt becomes positive. 'pre'/'pos' + ** allows some adjustments to be done only when needed. macro +diff --git a/lstate.c b/lstate.c +index 547a7a014..1ffe1a0f7 100644 +--- a/src/lstate.c ++++ b/src/lstate.c +@@ -236,7 +236,7 @@ static void f_luaopen (lua_State *L, void *ud) { + luaS_init(L); + luaT_init(L); + luaX_init(L); +- g->gcrunning = 1; /* allow gc */ ++ g->gcstp = 0; /* allow gc */ + setnilvalue(&g->nilvalue); /* now state is complete */ + luai_userstateopen(L); + } +@@ -373,7 +373,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { + g->ud_warn = NULL; + g->mainthread = L; + g->seed = luai_makeseed(L); +- g->gcrunning = 0; /* no GC while building state */ ++ g->gcstp = GCSTPGC; /* no GC while building state */ + g->strt.size = g->strt.nuse = 0; + g->strt.hash = NULL; + setnilvalue(&g->l_registry); +diff --git a/lstate.h b/lstate.h +index 44cf939cb..7886d8914 100644 +--- a/src/lstate.h ++++ b/src/lstate.h +@@ -263,7 +263,7 @@ typedef struct global_State { + lu_byte gcstopem; /* stops emergency collections */ + lu_byte genminormul; /* control for minor generational collections */ + lu_byte genmajormul; /* control for major generational collections */ +- lu_byte gcrunning; /* true if GC is running */ ++ lu_byte gcstp; /* control whether GC is running */ + lu_byte gcemergency; /* true if this is an emergency collection */ + lu_byte gcpause; /* size of pause between successive GCs */ + lu_byte gcstepmul; /* GC "speed" */ +From 597a53bbc681089d85b082b46c2e2428dec43b86 Mon Sep 17 00:00:00 2001 +From: Roberto Ierusalimschy +Date: Wed, 22 Dec 2021 09:00:52 -0300 +Subject: [PATCH] Bug: finalizer calling exit can corrupt finalization order + +'os.exit' can call lua_close again, separating new finalizers +created after all previous finalizers were already separated. +--- + lgc.c | 10 +++++----- + lgc.h | 1 + + testes/main.lua | 28 ++++++++++++++++++++++++++++ + 3 files changed, 34 insertions(+), 5 deletions(-) + +diff --git a/lgc.c b/lgc.c +index d3f5b5b7b..42a73d813 100644 +--- a/src/lgc.c ++++ b/src/lgc.c +@@ -907,7 +907,7 @@ static void GCTM (lua_State *L) { + int status; + lu_byte oldah = L->allowhook; + int oldgcstp = g->gcstp; +- g->gcstp = GCSTPGC; /* avoid GC steps */ ++ g->gcstp |= GCSTPGC; /* avoid GC steps */ + L->allowhook = 0; /* stop debug hooks during GC metamethod */ + setobj2s(L, L->top++, tm); /* push finalizer... */ + setobj2s(L, L->top++, &v); /* ... and its argument */ +@@ -1011,7 +1011,8 @@ static void correctpointers (global_State *g, GCObject *o) { + void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { + global_State *g = G(L); + if (tofinalize(o) || /* obj. is already marked... */ +- gfasttm(g, mt, TM_GC) == NULL) /* or has no finalizer? */ ++ gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */ ++ (g->gcstp & GCSTPCLS)) /* or closing state? */ + return; /* nothing to be done */ + else { /* move 'o' to 'finobj' list */ + GCObject **p; +@@ -1502,14 +1503,13 @@ static void deletelist (lua_State *L, GCObject *p, GCObject *limit) { + */ + void luaC_freeallobjects (lua_State *L) { + global_State *g = G(L); +- g->gcstp = GCSTPGC; ++ g->gcstp = GCSTPCLS; /* no extra finalizers after here */ + luaC_changemode(L, KGC_INC); + separatetobefnz(g, 1); /* separate all objects with finalizers */ + lua_assert(g->finobj == NULL); +- g->gcstp = 0; + callallpendingfinalizers(L); + deletelist(L, g->allgc, obj2gco(g->mainthread)); +- deletelist(L, g->finobj, NULL); ++ lua_assert(g->finobj == NULL); /* no new finalizers */ + deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ + lua_assert(g->strt.nuse == 0); + } +diff --git a/lgc.h b/lgc.h +index 024a4328e..4a125634b 100644 +--- a/src/lgc.h ++++ b/src/lgc.h +@@ -154,6 +154,7 @@ + */ + #define GCSTPUSR 1 /* bit true when GC stopped by user */ + #define GCSTPGC 2 /* bit true when GC stopped by itself */ ++#define GCSTPCLS 4 /* bit true when closing Lua state */ + #define gcrunning(g) ((g)->gcstp == 0) +