From c0adc5f8a5a2ab00139837291c914625df942257 Mon Sep 17 00:00:00 2001 From: Roberto I Date: Fri, 28 Aug 2026 09:27:47 -0300 Subject: [PATCH 1/6] Macro 'luaC_condGC' should call pre/pos only once With test option HARDMEMTESTS defined, luaC_condGC was calling pre and pos twice. That made macro checkGC set the stack top before the GC step and then again before the full collection, but the previous step could invalidate the pointer used to set top. --- lgc.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lgc.h b/lgc.h index ee054179..193ac0fc 100644 --- a/lgc.h +++ b/lgc.h @@ -231,8 +231,9 @@ #endif #define luaC_condGC(L,pre,pos) \ - { if (G(L)->GCdebt <= 0) { pre; luaC_step(L); pos;}; \ - condchangemem(L,pre,pos,0); } + { if (G(L)->GCdebt <= 0) \ + { pre; luaC_step(L); condchangemem(L,{},{},0); pos;} \ + else condchangemem(L,pre,pos,0); } /* more often than not, 'pre'/'pos' are empty */ #define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) From 35a87f8c5bdeeea285cef38002a962d2361e4807 Mon Sep 17 00:00:00 2001 From: Roberto I Date: Sun, 13 Sep 2026 14:08:50 -0300 Subject: [PATCH 2/6] Added casts to uses of 'sig_atomic' 'sig_atomic' can be larger than 'int' on some platforms. --- ldebug.c | 2 +- lvm.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ldebug.c b/ldebug.c index 5293ccb9..61c52749 100644 --- a/ldebug.c +++ b/ldebug.c @@ -150,7 +150,7 @@ LUA_API lua_Hook lua_gethook (lua_State *L) { LUA_API int lua_gethookmask (lua_State *L) { - return L->hookmask; + return cast_int(L->hookmask); } diff --git a/lvm.c b/lvm.c index 986c7db8..46170534 100644 --- a/lvm.c +++ b/lvm.c @@ -1117,7 +1117,7 @@ void luaV_finishOp (lua_State *L) { -#define updatetrap(ci) (trap = ci->u.l.trap) +#define updatetrap(ci) (trap = cast_int(ci->u.l.trap)) #define updatebase(ci) (base = ci->func.p + 1) @@ -1211,7 +1211,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { #include "ljumptab.h" #endif startfunc: - trap = L->hookmask; + trap = cast_int(L->hookmask); returning: /* trap already set */ cl = ci_func(ci); k = cl->p->k; From 4c32b2dfd204add9a3fed8d5eb100236e276e44a Mon Sep 17 00:00:00 2001 From: Roberto I Date: Tue, 15 Sep 2026 09:49:44 -0300 Subject: [PATCH 3/6] 'mktime' can return -1 --- loslib.c | 25 +++++++++++++++++-------- testes/files.lua | 7 +++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/loslib.c b/loslib.c index b7a2b0d1..fe9295c7 100644 --- a/loslib.c +++ b/loslib.c @@ -345,11 +345,22 @@ static int os_date (lua_State *L) { } +static int os_time_aux (lua_State *L, time_t t, int err) { + if (err || t != (time_t)(l_timet)t) + return luaL_error(L, + "time result cannot be represented in this installation"); + l_pushtime(L, t); + return 1; +} + + static int os_time (lua_State *L) { - time_t t; - if (lua_isnoneornil(L, 1)) /* called without args? */ - t = time(NULL); /* get current time */ + if (lua_isnoneornil(L, 1)) { /* called without args? */ + time_t t = time(NULL); /* get current time; error if it is -1 */ + return os_time_aux(L, t, (t == (time_t)(-1))); + } else { + time_t t; struct tm ts; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); /* make sure table is at the top */ @@ -360,14 +371,12 @@ static int os_time (lua_State *L) { ts.tm_min = getfield(L, "min", 0, 0); ts.tm_sec = getfield(L, "sec", 0, 0); ts.tm_isdst = getboolfield(L, "isdst"); + ts.tm_wday = -1; /* if call succeeds, it will set this value */ t = mktime(&ts); setallfields(L, &ts); /* update fields with normalized values */ + /* error if tm_wday was not set */ + return os_time_aux(L, t, ts.tm_wday == -1); } - if (t != (time_t)(l_timet)t || t == (time_t)(-1)) - return luaL_error(L, - "time result cannot be represented in this installation"); - l_pushtime(L, t); - return 1; } diff --git a/testes/files.lua b/testes/files.lua index 7146ac7c..04936565 100644 --- a/testes/files.lua +++ b/testes/files.lua @@ -854,6 +854,13 @@ local function checkDateTable (t) _G.D = nil end + +do -- testing mktime returning -1 + local t = os.date("*t", -1) + assert(os.time(t) == -1) +end + + checkDateTable(os.time()) if not _port then -- assume that time_t can represent these values From ceb32fb5bc29e3fe0009d54139081efbdf125cd4 Mon Sep 17 00:00:00 2001 From: Roberto I Date: Tue, 15 Sep 2026 10:03:36 -0300 Subject: [PATCH 4/6] Another try for silencing MSVS warning C4334 See also commit 8fac494. --- lobject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lobject.h b/lobject.h index bf5b65b4..82bd9703 100644 --- a/lobject.h +++ b/lobject.h @@ -827,7 +827,7 @@ typedef struct Table { #define twoto(x) (1u<<(x)) -#define sizenode(t) (twoto((t)->lsizenode)) +#define sizenode(t) cast_uint(twoto((t)->lsizenode)) /* size of buffer for 'luaO_utf8esc' function */ From 973777c74efe207ddddc6677b507007dea05dfdd Mon Sep 17 00:00:00 2001 From: Roberto I Date: Wed, 16 Sep 2026 11:49:35 -0300 Subject: [PATCH 5/6] Details --- lmathlib.c | 44 ++++++++++++++++++++++---------------------- loslib.c | 20 ++++++++++---------- manual/manual.of | 4 ++-- 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lmathlib.c b/lmathlib.c index a6b13f96..87e7462a 100644 --- a/lmathlib.c +++ b/lmathlib.c @@ -704,35 +704,35 @@ static int math_log10 (lua_State *L) { static const luaL_Reg mathlib[] = { - {"abs", math_abs}, - {"acos", math_acos}, - {"asin", math_asin}, - {"atan", math_atan}, - {"ceil", math_ceil}, - {"cos", math_cos}, - {"deg", math_deg}, - {"exp", math_exp}, + {"abs", math_abs}, + {"acos", math_acos}, + {"asin", math_asin}, + {"atan", math_atan}, + {"ceil", math_ceil}, + {"cos", math_cos}, + {"deg", math_deg}, + {"exp", math_exp}, {"tointeger", math_toint}, {"floor", math_floor}, - {"fmod", math_fmod}, + {"fmod", math_fmod}, {"frexp", math_frexp}, - {"ult", math_ult}, + {"ult", math_ult}, {"ldexp", math_ldexp}, - {"log", math_log}, - {"max", math_max}, - {"min", math_min}, - {"modf", math_modf}, - {"rad", math_rad}, - {"sin", math_sin}, - {"sqrt", math_sqrt}, - {"tan", math_tan}, + {"log", math_log}, + {"max", math_max}, + {"min", math_min}, + {"modf", math_modf}, + {"rad", math_rad}, + {"sin", math_sin}, + {"sqrt", math_sqrt}, + {"tan", math_tan}, {"type", math_type}, #if defined(LUA_COMPAT_MATHLIB) {"atan2", math_atan}, - {"cosh", math_cosh}, - {"sinh", math_sinh}, - {"tanh", math_tanh}, - {"pow", math_pow}, + {"cosh", math_cosh}, + {"sinh", math_sinh}, + {"tanh", math_tanh}, + {"pow", math_pow}, {"log10", math_log10}, #endif /* placeholders */ diff --git a/loslib.c b/loslib.c index fe9295c7..111ac2f8 100644 --- a/loslib.c +++ b/loslib.c @@ -416,17 +416,17 @@ static int os_exit (lua_State *L) { static const luaL_Reg syslib[] = { - {"clock", os_clock}, - {"date", os_date}, - {"difftime", os_difftime}, - {"execute", os_execute}, - {"exit", os_exit}, - {"getenv", os_getenv}, - {"remove", os_remove}, - {"rename", os_rename}, + {"clock", os_clock}, + {"date", os_date}, + {"difftime", os_difftime}, + {"execute", os_execute}, + {"exit", os_exit}, + {"getenv", os_getenv}, + {"remove", os_remove}, + {"rename", os_rename}, {"setlocale", os_setlocale}, - {"time", os_time}, - {"tmpname", os_tmpname}, + {"time", os_time}, + {"tmpname", os_tmpname}, {NULL, NULL} }; diff --git a/manual/manual.of b/manual/manual.of index e2d1a651..e3c7cd25 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -6071,8 +6071,8 @@ In both cases, the function pushes onto the stack the final value associated with @id{tname} in the registry. -Usage note: Beware the use of the return value of this function to -conditionally initializes the new metatable +Usage note: Beware the use of the return value of this function +to conditionally initialize the new metatable (e.g., by adding metamethods to it). If the initialization raises an error, the metatable will not be properly initialized, From 0b29f408433e92953cc72b1d3e06c7ac8139e439 Mon Sep 17 00:00:00 2001 From: Roberto I Date: Wed, 16 Sep 2026 14:18:11 -0300 Subject: [PATCH 6/6] Bug: UB when applying GC parameter When computing whether Lua should return from gen-major to gen-minor, the difference between the total memory and the previous total memory can be negative, which can result in that negative value being left-shifted (UB). --- lgc.c | 3 ++- lobject.c | 1 + testes/gengc.lua | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/lgc.c b/lgc.c index a463e41e..cf4160c1 100644 --- a/lgc.c +++ b/lgc.c @@ -1472,7 +1472,8 @@ static int checkmajorminor (lua_State *L, global_State *g) { if (g->gckind == KGC_GENMAJOR) { /* generational mode? */ l_mem numbytes = gettotalbytes(g); l_mem addedbytes = numbytes - g->GCmajorminor; - l_mem limit = applygcparam(g, MAJORMINOR, addedbytes); + l_mem limit = (addedbytes < 0) ? 0 + : applygcparam(g, MAJORMINOR, addedbytes); l_mem tobecollected = numbytes - g->GCmarked; if (tobecollected > limit) { atomic2gen(L, g); /* return to generational mode */ diff --git a/lobject.c b/lobject.c index 763b4846..6997cfd0 100644 --- a/lobject.c +++ b/lobject.c @@ -89,6 +89,7 @@ lu_byte luaO_codeparam (unsigned int p) { l_mem luaO_applyparam (lu_byte p, l_mem x) { int m = p & 0xF; /* mantissa */ int e = (p >> 4); /* exponent */ + lua_assert(x >= 0); if (e > 0) { /* normalized? */ e--; /* correct exponent */ m += 0x10; /* correct mantissa; maximum value is 0x1F */ diff --git a/testes/gengc.lua b/testes/gengc.lua index 6509e39d..84c1a8ee 100644 --- a/testes/gengc.lua +++ b/testes/gengc.lua @@ -162,6 +162,51 @@ end assert(collectgarbage'isrunning') +do + -- bug in 5.0: when computing whether it should return from gen-major + -- to gen-minor, the difference between the total memory and the + -- previous total memory can be negative, which results in that + -- negative value being left-shifted (UB) + + local lim = 1e6 + + -- make major collections non-incremental + local oldsm = collectgarbage("param", "stepmul", 0) + + -- make "majorminor" large enough to force a left-shift + -- when applying the parameter (internal details) + local oldmm = collectgarbage("param", "majorminor", 2000) + + collectgarbage(); collectgarbage() + assert(not T or T.gcquery() == "genminor") + + local M = collectgarbage"count" * 1024 + + -- create a large table + local t = {} + for i = 1, lim do t[i] = true end + assert(collectgarbage"count" * 1024 > M + lim * string.packsize"j") + + -- force collector to "generational major" mode, doing several + -- minor collections that recover no memory + collectgarbage"step"; collectgarbage"step"; collectgarbage"step" + assert(not T or T.gcquery() == "genmajor") + + -- shrink the table + for i = 1, lim do t[i] = nil end + t[2 * lim] = true + assert(collectgarbage"count" < M * 5/4) + + -- bug was here, an assert violation when checking whether to + -- return to 'genminor' + collectgarbage"step" + + -- restore previous parameters + collectgarbage("param", "stepmul", oldsm) + collectgarbage("param", "majorminor", oldmm) +end + + do print"testing stop-the-world collection" local step = collectgarbage("param", "stepsize", 0); collectgarbage("incremental")