liblua54: patches for CVE-2020-15888, CVE-2020-15889, CVE-2020-15945

This commit is contained in:
Henrik Grimler 2020-10-01 16:18:11 +03:00 committed by Leonid Pliushch
parent d7473dee2c
commit cbe151a237
3 changed files with 376 additions and 0 deletions

View File

@ -0,0 +1,154 @@
commit 6298903e35217ab69c279056f925fb72900ce0b7
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date: Mon Jul 6 12:11:54 2020 -0300
Keep minimum size when shrinking a stack
When shrinking a stack (during GC), do not make it smaller than the
initial stack size.
diff --git a/src/ldo.c b/src/ldo.c
index c563b1d9..a89ac010 100644
--- a/src/ldo.c
+++ b/src/ldo.c
@@ -245,13 +245,12 @@ static int stackinuse (lua_State *L) {
void luaD_shrinkstack (lua_State *L) {
int inuse = stackinuse(L);
- int goodsize = inuse + (inuse / 8) + 2*EXTRA_STACK;
+ int goodsize = inuse + BASIC_STACK_SIZE;
if (goodsize > LUAI_MAXSTACK)
goodsize = LUAI_MAXSTACK; /* respect stack limit */
/* if thread is currently not handling a stack overflow and its
good size is smaller than current size, shrink its stack */
- if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) &&
- goodsize < L->stacksize)
+ if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && goodsize < L->stacksize)
luaD_reallocstack(L, goodsize, 0); /* ok if that fails */
else /* don't change stack */
condmovestack(L,{},{}); /* (change only for debugging) */
commit eb41999461b6f428186c55abd95f4ce1a76217d5
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date: Tue Jul 7 18:03:48 2020 -0300
Fixed bugs of stack reallocation x GC
Macro 'checkstackGC' was doing a GC step after resizing the stack;
the GC could shrink the stack and undo the resize. Moreover, macro
'checkstackp' also does a GC step, which could remove the preallocated
CallInfo when calling a function. (Its name has been changed to
'checkstackGCp' to emphasize that it calls the GC.)
diff --git a/src/ldo.c b/src/ldo.c
index 66217a4b..e3db1f74 100644
--- a/src/ldo.c
+++ b/src/ldo.c
@@ -465,13 +465,13 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
f = fvalue(s2v(func));
Cfunc: {
int n; /* number of returns */
- CallInfo *ci = next_ci(L);
- checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
+ CallInfo *ci;
+ checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
+ L->ci = ci = next_ci(L);
ci->nresults = nresults;
ci->callstatus = CIST_C;
ci->top = L->top + LUA_MINSTACK;
ci->func = func;
- L->ci = ci;
lua_assert(ci->top <= L->stack_last);
if (L->hookmask & LUA_MASKCALL) {
int narg = cast_int(L->top - func) - 1;
@@ -485,12 +485,13 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
break;
}
case LUA_VLCL: { /* Lua function */
- CallInfo *ci = next_ci(L);
+ CallInfo *ci;
Proto *p = clLvalue(s2v(func))->p;
int narg = cast_int(L->top - func) - 1; /* number of real arguments */
int nfixparams = p->numparams;
int fsize = p->maxstacksize; /* frame size */
- checkstackp(L, fsize, func);
+ checkstackGCp(L, fsize, func);
+ L->ci = ci = next_ci(L);
ci->nresults = nresults;
ci->u.l.savedpc = p->code; /* starting point */
ci->callstatus = 0;
@@ -504,7 +505,7 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
break;
}
default: { /* not a function */
- checkstackp(L, 1, func); /* space for metamethod */
+ checkstackGCp(L, 1, func); /* space for metamethod */
luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
goto retry; /* try again with metamethod */
}
diff --git a/src/ldo.h b/src/ldo.h
index 7760f853..6c6cb285 100644
--- a/src/ldo.h
+++ b/src/ldo.h
@@ -17,6 +17,8 @@
** Macro to check stack size and grow stack if needed. Parameters
** 'pre'/'pos' allow the macro to preserve a pointer into the
** stack across reallocations, doing the work only when needed.
+** It also allows the running of one GC step when the stack is
+** reallocated.
** 'condmovestack' is used in heavy tests to force a stack reallocation
** at every check.
*/
@@ -35,7 +37,7 @@
/* macro to check stack size, preserving 'p' */
-#define checkstackp(L,n,p) \
+#define checkstackGCp(L,n,p) \
luaD_checkstackaux(L, n, \
ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
luaC_checkGC(L), /* stack grow uses memory */ \
@@ -44,7 +46,7 @@
/* macro to check stack size and GC */
#define checkstackGC(L,fsize) \
- luaD_checkstackaux(L, (fsize), (void)0, luaC_checkGC(L))
+ luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0)
/* type of protected functions, to be ran by 'runprotected' */
diff --git a/src/ltm.c b/src/ltm.c
index ae60983f..4770f96b 100644
--- a/src/ltm.c
+++ b/src/ltm.c
@@ -240,7 +240,7 @@ void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */
int nextra = actual - nfixparams; /* number of extra arguments */
ci->u.l.nextraargs = nextra;
- checkstackGC(L, p->maxstacksize + 1);
+ luaD_checkstack(L, p->maxstacksize + 1);
/* copy function to the top of the stack */
setobjs2s(L, L->top++, ci->func);
/* move fixed parameters to the top of the stack */
@@ -259,7 +259,7 @@ void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
int nextra = ci->u.l.nextraargs;
if (wanted < 0) {
wanted = nextra; /* get all extra arguments available */
- checkstackp(L, nextra, where); /* ensure stack space */
+ checkstackGCp(L, nextra, where); /* ensure stack space */
L->top = where + nextra; /* next instruction will need top */
}
for (i = 0; i < wanted && i < nextra; i++)
diff --git a/src/lvm.c b/src/lvm.c
index ccbfbab5..d78d6be2 100644
--- a/src/lvm.c
+++ b/src/lvm.c
@@ -1634,7 +1634,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
while (!ttisfunction(s2v(ra))) { /* not a function? */
luaD_tryfuncTM(L, ra); /* try '__call' metamethod */
b++; /* there is now one extra argument */
- checkstackp(L, 1, ra);
+ checkstackGCp(L, 1, ra);
}
if (!ttisLclosure(s2v(ra))) { /* C function? */
luaD_call(L, ra, LUA_MULTRET); /* call it */

View File

@ -0,0 +1,35 @@
commit 127e7a6c8942b362aa3c6627f44d660a4fb75312
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date: Fri Jul 10 14:13:50 2020 -0300
Fixed bug of old finalized objects in the GC
When an object aged OLD1 is finalized, it is moved from the list
'finobj' to the *beginning* of the list 'allgc'. So, this part of the
list (and not only the survival list) must be visited by 'markold'.
diff --git a/src/lgc.c b/src/lgc.c
index f26c921a..f7fd7a59 100644
--- a/src/lgc.c
+++ b/src/lgc.c
@@ -1131,16 +1131,14 @@ static void finishgencycle (lua_State *L, global_State *g) {
/*
-** Does a young collection. First, mark 'OLD1' objects. (Only survival
-** and "recent old" lists can contain 'OLD1' objects. New lists cannot
-** contain 'OLD1' objects, at most 'OLD0' objects that were already
-** visited when marked old.) Then does the atomic step. Then,
-** sweep all lists and advance pointers. Finally, finish the collection.
+** Does a young collection. First, mark 'OLD1' objects. Then does the
+** atomic step. Then, sweep all lists and advance pointers. Finally,
+** finish the collection.
*/
static void youngcollection (lua_State *L, global_State *g) {
GCObject **psurvival; /* to point to first non-dead survival object */
lua_assert(g->gcstate == GCSpropagate);
- markold(g, g->survival, g->reallyold);
+ markold(g, g->allgc, g->reallyold);
markold(g, g->finobj, g->finobjrold);
atomic(L);

View File

@ -0,0 +1,187 @@
commit a2195644d89812e5b157ce7bac35543e06db05e3
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date: Fri Jul 17 11:01:05 2020 -0300
Fixed bug: invalid 'oldpc' when returning to a function
The field 'L->oldpc' is not always updated when control returns to a
function; an invalid value can seg. fault when computing 'changedline'.
(One example is an error in a finalizer; control can return to
'luaV_execute' without executing 'luaD_poscall'.) Instead of trying to
fix all possible corner cases, it seems safer to be resilient to invalid
values for 'oldpc'. Valid but wrong values at most cause an extra call
to a line hook.
diff --git a/src/ldebug.c b/src/ldebug.c
index afdc2b74..0c4439c1 100644
--- a/src/ldebug.c
+++ b/src/ldebug.c
@@ -33,10 +33,8 @@
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
-
-/* Active Lua function (given call info) */
-#define ci_func(ci) (clLvalue(s2v((ci)->func)))
-
+/* inverse of 'pcRel' */
+#define invpcRel(pc, p) ((p)->code + (pc) + 1)
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
const char **name);
@@ -127,20 +125,18 @@ static void settraps (CallInfo *ci) {
/*
** This function can be called during a signal, under "reasonable"
** assumptions.
-** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by
-** 'resethookcount') are for debug only, and it is no problem if they
-** get arbitrary values (causes at most one wrong hook call). 'hookmask'
-** is an atomic value. We assume that pointers are atomic too (e.g., gcc
-** ensures that for all platforms where it runs). Moreover, 'hook' is
-** always checked before being called (see 'luaD_hook').
+** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
+** are for debug only, and it is no problem if they get arbitrary
+** values (causes at most one wrong hook call). 'hookmask' is an atomic
+** value. We assume that pointers are atomic too (e.g., gcc ensures that
+** for all platforms where it runs). Moreover, 'hook' is always checked
+** before being called (see 'luaD_hook').
*/
LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
if (func == NULL || mask == 0) { /* turn off hooks? */
mask = 0;
func = NULL;
}
- if (isLua(L->ci))
- L->oldpc = L->ci->u.l.savedpc;
L->hook = func;
L->basehookcount = count;
resethookcount(L);
@@ -795,10 +791,24 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
}
+/*
+** Traces the execution of a Lua function. Called before the execution
+** of each opcode, when debug is on. 'L->oldpc' stores the last
+** instruction traced, to detect line changes. When entering a new
+** function, 'npci' will be zero and will test as a new line without
+** the need for 'oldpc'; so, 'oldpc' does not need to be initialized
+** before. Some exceptional conditions may return to a function without
+** updating 'oldpc'. In that case, 'oldpc' may be invalid; if so, it is
+** reset to zero. (A wrong but valid 'oldpc' at most causes an extra
+** call to a line hook.)
+*/
int luaG_traceexec (lua_State *L, const Instruction *pc) {
CallInfo *ci = L->ci;
lu_byte mask = L->hookmask;
+ const Proto *p = ci_func(ci)->p;
int counthook;
+ /* 'L->oldpc' may be invalid; reset it in this case */
+ int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
ci->u.l.trap = 0; /* don't need to stop again */
return 0; /* turn off 'trap' */
@@ -819,15 +829,14 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
if (counthook)
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
if (mask & LUA_MASKLINE) {
- const Proto *p = ci_func(ci)->p;
int npci = pcRel(pc, p);
if (npci == 0 || /* call linehook when enter a new function, */
- pc <= L->oldpc || /* when jump back (loop), or when */
- changedline(p, pcRel(L->oldpc, p), npci)) { /* enter new line */
+ pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
+ changedline(p, oldpc, npci)) { /* enter new line */
int newline = luaG_getfuncline(p, npci);
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
}
- L->oldpc = pc; /* 'pc' of last call to line hook */
+ L->oldpc = npci; /* 'pc' of last call to line hook */
}
if (L->status == LUA_YIELD) { /* did hook yield? */
if (counthook)
diff --git a/src/ldebug.h b/src/ldebug.h
index 1fe0efab..a0a58486 100644
--- a/src/ldebug.h
+++ b/src/ldebug.h
@@ -13,6 +13,11 @@
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
+
+/* Active Lua function (given call info) */
+#define ci_func(ci) (clLvalue(s2v((ci)->func)))
+
+
#define resethookcount(L) (L->hookcount = L->basehookcount)
/*
diff --git a/src/ldo.c b/src/ldo.c
index 4c976a14..98dd9fbb 100644
--- a/src/ldo.c
+++ b/src/ldo.c
@@ -327,7 +327,7 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
int delta = 0;
if (isLuacode(ci)) {
- Proto *p = clLvalue(s2v(ci->func))->p;
+ Proto *p = ci_func(ci)->p;
if (p->is_vararg)
delta = ci->u.l.nextraargs + p->numparams + 1;
if (L->top < ci->top)
@@ -340,8 +340,8 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
ci->func -= delta;
}
- if (isLua(ci->previous))
- L->oldpc = ci->previous->u.l.savedpc; /* update 'oldpc' */
+ if (isLua(ci = ci->previous))
+ L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
return restorestack(L, oldtop);
}
diff --git a/src/lstate.c b/src/lstate.c
index 28853dc7..06fa13d7 100644
--- a/src/lstate.c
+++ b/src/lstate.c
@@ -301,6 +301,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
L->openupval = NULL;
L->status = LUA_OK;
L->errfunc = 0;
+ L->oldpc = 0;
}
diff --git a/src/lstate.h b/src/lstate.h
index 2e8bd6c4..0c545ec5 100644
--- a/src/lstate.h
+++ b/src/lstate.h
@@ -286,7 +286,6 @@ struct lua_State {
StkId top; /* first free slot in the stack */
global_State *l_G;
CallInfo *ci; /* call info for current function */
- const Instruction *oldpc; /* last pc traced */
StkId stack_last; /* last free slot in the stack */
StkId stack; /* stack base */
UpVal *openupval; /* list of open upvalues in this stack */
@@ -297,6 +296,7 @@ struct lua_State {
volatile lua_Hook hook;
ptrdiff_t errfunc; /* current error handling function (stack index) */
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
+ int oldpc; /* last pc traced */
int stacksize;
int basehookcount;
int hookcount;
diff --git a/src/lvm.c b/src/lvm.c
index 66d451b0..08681af1 100644
--- a/src/lvm.c
+++ b/src/lvm.c
@@ -1794,7 +1794,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
if (trap) {
luaD_hookcall(L, ci);
- L->oldpc = pc + 1; /* next opcode will be seen as a "new" line */
+ L->oldpc = 1; /* next opcode will be seen as a "new" line */
}
updatebase(ci); /* function has new base after adjustment */
vmbreak;