diff --git a/lapi.c b/lapi.c index 3098cb9c..d6aaf8a8 100644 --- a/lapi.c +++ b/lapi.c @@ -1,5 +1,5 @@ /* -** $Id: lapi.c,v 2.277 2017/11/23 19:29:04 roberto Exp roberto $ +** $Id: lapi.c,v 2.278 2017/12/06 18:08:03 roberto Exp roberto $ ** Lua API ** See Copyright Notice in lua.h */ @@ -99,16 +99,6 @@ static StkId index2stack (lua_State *L, int idx) { } -/* -** to be called by 'lua_checkstack' in protected mode, to grow stack -** capturing memory errors -*/ -static void growstack (lua_State *L, void *ud) { - int size = *(int *)ud; - luaD_growstack(L, size); -} - - LUA_API int lua_checkstack (lua_State *L, int n) { int res; CallInfo *ci = L->ci; @@ -121,7 +111,7 @@ LUA_API int lua_checkstack (lua_State *L, int n) { if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */ res = 0; /* no */ else /* try to grow stack */ - res = (luaD_rawrunprotected(L, &growstack, &n) == LUA_OK); + res = luaD_growstack(L, n, 0); } if (res && ci->top < L->top + n) ci->top = L->top + n; /* adjust frame top */ diff --git a/ldo.c b/ldo.c index fe1fcb25..136aef55 100644 --- a/ldo.c +++ b/ldo.c @@ -1,5 +1,5 @@ /* -** $Id: ldo.c,v 2.176 2017/11/29 13:02:17 roberto Exp roberto $ +** $Id: ldo.c,v 2.177 2017/12/01 15:44:51 roberto Exp roberto $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -156,17 +156,17 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { ** Stack reallocation ** =================================================================== */ -static void correctstack (lua_State *L, StkId oldstack) { +static void correctstack (lua_State *L, StkId oldstack, StkId newstack) { CallInfo *ci; UpVal *up; - if (L->stack == oldstack) + if (oldstack == newstack) return; /* stack address did not change */ - L->top = (L->top - oldstack) + L->stack; + L->top = (L->top - oldstack) + newstack; for (up = L->openupval; up != NULL; up = up->u.open.next) - up->v = s2v((uplevel(up) - oldstack) + L->stack); + up->v = s2v((uplevel(up) - oldstack) + newstack); for (ci = L->ci; ci != NULL; ci = ci->previous) { - ci->top = (ci->top - oldstack) + L->stack; - ci->func = (ci->func - oldstack) + L->stack; + ci->top = (ci->top - oldstack) + newstack; + ci->func = (ci->func - oldstack) + newstack; if (isLua(ci)) ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ } @@ -177,36 +177,40 @@ static void correctstack (lua_State *L, StkId oldstack) { #define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) -void luaD_reallocstack (lua_State *L, int newsize) { - StkId oldstack = L->stack; +int luaD_reallocstack (lua_State *L, int newsize, int safe) { int lim = L->stacksize; + StkId newstack = luaM_reallocvector(L, L->stack, lim, newsize, StackValue); lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK); - luaM_reallocvector(L, L->stack, L->stacksize, newsize, StackValue); + if (newstack == NULL) { /* reallocation failed? */ + if (safe) luaM_error(L); + else return 0; /* no-safe mode: signal the error */ + } for (; lim < newsize; lim++) - setnilvalue(s2v(L->stack + lim)); /* erase new segment */ + setnilvalue(s2v(newstack + lim)); /* erase new segment */ + correctstack(L, L->stack, newstack); + L->stack = newstack; L->stacksize = newsize; L->stack_last = L->stack + newsize - EXTRA_STACK; - correctstack(L, oldstack); + return 1; } -void luaD_growstack (lua_State *L, int n) { +int luaD_growstack (lua_State *L, int n, int safe) { int size = L->stacksize; + int newsize = 2 * size; if (size > LUAI_MAXSTACK) /* error after extra size? */ luaD_throw(L, LUA_ERRERR); else { int needed = cast_int(L->top - L->stack) + n + EXTRA_STACK; - int newsize = 2 * size; if (newsize > LUAI_MAXSTACK) newsize = LUAI_MAXSTACK; if (newsize < needed) newsize = needed; if (newsize > LUAI_MAXSTACK) { /* stack overflow? */ - luaD_reallocstack(L, ERRORSTACKSIZE); + luaD_reallocstack(L, ERRORSTACKSIZE, 1); luaG_runerror(L, "stack overflow"); } - else - luaD_reallocstack(L, newsize); - } + } /* else */ + return luaD_reallocstack(L, newsize, safe); } @@ -234,7 +238,7 @@ void luaD_shrinkstack (lua_State *L) { good size is smaller than current size, shrink its stack */ if (inuse <= (LUAI_MAXSTACK - EXTRA_STACK) && goodsize < L->stacksize) - luaD_reallocstack(L, goodsize); + luaD_reallocstack(L, goodsize, 0); /* ok if that fails */ else /* don't change stack */ condmovestack(L,{},{}); /* (change only for debugging) */ } diff --git a/ldo.h b/ldo.h index a9b46cdf..388f6c3e 100644 --- a/ldo.h +++ b/ldo.h @@ -1,5 +1,5 @@ /* -** $Id: ldo.h,v 2.35 2017/11/23 16:35:54 roberto Exp roberto $ +** $Id: ldo.h,v 2.36 2017/11/23 18:29:41 roberto Exp roberto $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -22,7 +22,8 @@ */ #define luaD_checkstackaux(L,n,pre,pos) \ if (L->stack_last - L->top <= (n)) \ - { pre; luaD_growstack(L, n); pos; } else { condmovestack(L,pre,pos); } + { pre; luaD_growstack(L, n, 1); pos; } \ + else { condmovestack(L,pre,pos); } /* In general, 'pre'/'pos' are empty (nothing to save) */ #define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0) @@ -55,8 +56,8 @@ LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t oldtop, ptrdiff_t ef); LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, int nres); -LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); -LUAI_FUNC void luaD_growstack (lua_State *L, int n); +LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int safe); +LUAI_FUNC int luaD_growstack (lua_State *L, int n, int safe); LUAI_FUNC void luaD_shrinkstack (lua_State *L); LUAI_FUNC void luaD_inctop (lua_State *L); diff --git a/lgc.c b/lgc.c index f893e122..8d28d3bf 100644 --- a/lgc.c +++ b/lgc.c @@ -1,5 +1,5 @@ /* -** $Id: lgc.c,v 2.240 2017/11/30 15:37:16 roberto Exp roberto $ +** $Id: lgc.c,v 2.241 2017/12/01 17:38:49 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -816,18 +816,13 @@ static GCObject **sweeptolive (lua_State *L, GCObject **p) { */ /* -** If possible, shrink string table (protected from memory errors). +** If possible, shrink string table. */ -static void shrinkstrtable (lua_State *L, void *ud) { - luaS_resize(L, *cast(int*, ud) / 2); -} - - static void checkSizes (lua_State *L, global_State *g) { if (!g->gcemergency) { l_mem olddebt = g->GCdebt; if (g->strt.nuse < g->strt.size / 4) /* string table too big? */ - luaD_rawrunprotected(L, &shrinkstrtable, &g->strt.size); + luaS_resize(L, g->strt.size / 2); g->GCestimate += g->GCdebt - olddebt; /* correct estimate */ } } diff --git a/lmem.c b/lmem.c index 23dc14d6..afacbb9f 100644 --- a/lmem.c +++ b/lmem.c @@ -1,5 +1,5 @@ /* -** $Id: lmem.c,v 1.92 2017/12/06 18:36:31 roberto Exp roberto $ +** $Id: lmem.c,v 1.93 2017/12/07 18:59:52 roberto Exp roberto $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ @@ -69,9 +69,12 @@ void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize, if (size < MINSIZEARRAY) size = MINSIZEARRAY; /* minimum size */ } + lua_assert(nelems + 1 <= size && size <= limit); /* 'limit' ensures that multiplication will not overflow */ - newblock = luaM_realloc(L, block, cast(size_t, *psize) * size_elems, - cast(size_t, size) * size_elems); + newblock = luaM_realloc_(L, block, cast(size_t, *psize) * size_elems, + cast(size_t, size) * size_elems); + if (newblock == NULL) + luaM_error(L); *psize = size; /* update only when everything else is OK */ return newblock; } @@ -115,20 +118,20 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) { /* ** generic allocation routine. */ -void *luaM_realloc (lua_State *L, void *block, size_t osize, size_t nsize) { +void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { void *newblock; global_State *g = G(L); lua_assert((osize == 0) == (block == NULL)); hardtest(L, osize, nsize); newblock = (*g->frealloc)(g->ud, block, osize, nsize); if (newblock == NULL && nsize > 0) { - lua_assert(nsize > osize); /* cannot fail when shrinking a block */ - if (g->version) { /* is state fully built? */ + /* Is state fully built? Not shrinking a block? */ + if (g->version && nsize > osize) { luaC_fullgc(L, 1); /* try to free some memory... */ newblock = (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ } if (newblock == NULL) - luaD_throw(L, LUA_ERRMEM); + return NULL; } lua_assert((nsize == 0) == (newblock == NULL)); g->GCdebt = (g->GCdebt + nsize) - osize; @@ -136,7 +139,16 @@ void *luaM_realloc (lua_State *L, void *block, size_t osize, size_t nsize) { } -void *luaM_malloc (lua_State *L, size_t size, int tag) { +void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize, + size_t nsize) { + void *newblock = luaM_realloc_(L, block, osize, nsize); + if (newblock == NULL && nsize > 0) /* allocation failed? */ + luaM_error(L); + return newblock; +} + + +void *luaM_malloc_ (lua_State *L, size_t size, int tag) { hardtest(L, 0, size); if (size == 0) return NULL; /* that's all */ @@ -149,7 +161,7 @@ void *luaM_malloc (lua_State *L, size_t size, int tag) { newblock = (*g->frealloc)(g->ud, NULL, tag, size); /* try again */ } if (newblock == NULL) - luaD_throw(L, LUA_ERRMEM); + luaM_error(L); } g->GCdebt += size; return newblock; diff --git a/lmem.h b/lmem.h index 3c828789..e98aabdb 100644 --- a/lmem.h +++ b/lmem.h @@ -1,5 +1,5 @@ /* -** $Id: lmem.h,v 1.44 2017/12/06 18:36:31 roberto Exp roberto $ +** $Id: lmem.h,v 1.45 2017/12/07 18:59:52 roberto Exp roberto $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ @@ -14,6 +14,9 @@ #include "lua.h" +#define luaM_error(L) luaD_throw(L, LUA_ERRMEM) + + /* ** This macro tests whether it is safe to multiply 'n' by the size of ** type 't' without overflows. Because 'e' is always constant, it avoids @@ -45,26 +48,26 @@ ** Arrays of chars do not need any test */ #define luaM_reallocvchar(L,b,on,n) \ - cast(char *, luaM_realloc(L, (b), (on)*sizeof(char), (n)*sizeof(char))) + cast(char *, luaM_saferealloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char))) #define luaM_freemem(L, b, s) luaM_free_(L, (b), (s)) #define luaM_free(L, b) luaM_free_(L, (b), sizeof(*(b))) #define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b))) -#define luaM_new(L,t) cast(t*, luaM_malloc(L, sizeof(t), 0)) -#define luaM_newvector(L,n,t) cast(t*, luaM_malloc(L, (n)*sizeof(t), 0)) +#define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0)) +#define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0)) #define luaM_newvectorchecked(L,n,t) \ (luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t)) -#define luaM_newobject(L,tag,s) luaM_malloc(L, (s), tag) +#define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) #define luaM_growvector(L,v,nelems,size,t,limit,e) \ ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ luaM_limitN(limit,t),e))) #define luaM_reallocvector(L, v,oldn,n,t) \ - ((v)=cast(t *, luaM_realloc(L, v, cast(size_t, oldn) * sizeof(t), \ - cast(size_t, n) * sizeof(t)))) + (cast(t *, luaM_realloc_(L, v, cast(size_t, oldn) * sizeof(t), \ + cast(size_t, n) * sizeof(t)))) #define luaM_shrinkvector(L,v,size,fs,t) \ ((v)=cast(t *, luaM_shrinkvector_(L, v, &(size), fs, sizeof(t)))) @@ -72,15 +75,17 @@ LUAI_FUNC l_noret luaM_toobig (lua_State *L); /* not to be called directly */ -LUAI_FUNC void *luaM_realloc (lua_State *L, void *block, size_t oldsize, +LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, size_t size); +LUAI_FUNC void *luaM_saferealloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); LUAI_FUNC void luaM_free_ (lua_State *L, void *block, size_t osize); LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *size, int size_elem, int limit, const char *what); LUAI_FUNC void *luaM_shrinkvector_ (lua_State *L, void *block, int *nelem, int final_n, int size_elem); -LUAI_FUNC void *luaM_malloc (lua_State *L, size_t size, int tag); +LUAI_FUNC void *luaM_malloc_ (lua_State *L, size_t size, int tag); #endif diff --git a/lstring.c b/lstring.c index 1b3f53e1..1675b87a 100644 --- a/lstring.c +++ b/lstring.c @@ -1,5 +1,5 @@ /* -** $Id: lstring.c,v 2.58 2017/12/01 16:40:29 roberto Exp roberto $ +** $Id: lstring.c,v 2.59 2017/12/07 18:59:52 roberto Exp roberto $ ** String table (keeps all strings handled by Lua) ** See Copyright Notice in lua.h */ @@ -70,12 +70,15 @@ unsigned int luaS_hashlongstr (TString *ts) { /* -** Resizes the string table. +** Resize the string table. If allocation fails, keep the current size. +** (This can degrade performance, but any size should work correctly.) */ void luaS_resize (lua_State *L, int newsize) { int i; TString **newhash = luaM_newvector(L, newsize, TString *); stringtable *tb = &G(L)->strt; + if (newhash == NULL) /* allocation failed? */ + return; /* leave hash as it is */ for (i = 0; i < newsize; i++) /* initialize new hash array */ newhash[i] = NULL; for (i = 0; i < tb->size; i++) { /* rehash all elements into new array */ diff --git a/ltable.c b/ltable.c index d1345009..85101a8a 100644 --- a/ltable.c +++ b/ltable.c @@ -1,5 +1,5 @@ /* -** $Id: ltable.c,v 2.127 2017/11/23 19:29:04 roberto Exp roberto $ +** $Id: ltable.c,v 2.128 2017/12/07 18:59:52 roberto Exp roberto $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ @@ -357,15 +357,6 @@ static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) { } -static void setarrayvector (lua_State *L, Table *t, unsigned int size) { - unsigned int i; - luaM_reallocvector(L, t->array, t->sizearray, size, TValue); - for (i=t->sizearray; iarray[i]); - t->sizearray = size; -} - - /* ** Creates an array for the hash part of a table with the given ** size, or reuses the dummy node if size is zero. @@ -398,39 +389,79 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) { } -void luaH_resize (lua_State *L, Table *t, unsigned int nasize, - unsigned int nhsize) { - unsigned int i; +/* +** (Re)insert all elements from list 'nodes' into table 't'. +*/ +static void reinsert(lua_State *L, Node *nodes, int nsize, Table *t) { int j; - unsigned int oldasize = t->sizearray; - int oldhsize = allocsizenode(t); - Node *nold = t->node; /* save old hash ... */ - if (nasize > oldasize) /* array part must grow? */ - setarrayvector(L, t, nasize); - /* create new hash part with appropriate size */ - setnodevector(L, t, nhsize); - if (nasize < oldasize) { /* array part must shrink? */ - t->sizearray = nasize; - /* re-insert elements from vanishing slice */ - for (i=nasize; iarray[i])) - luaH_setint(L, t, i + 1, &t->array[i]); - } - /* shrink array */ - luaM_reallocvector(L, t->array, oldasize, nasize, TValue); - } - /* re-insert elements from hash part */ - for (j = oldhsize - 1; j >= 0; j--) { - Node *old = nold + j; + for (j = nsize - 1; j >= 0; j--) { + Node *old = nodes + j; if (!ttisnil(gval(old))) { /* doesn't need barrier/invalidate cache, as entry was already present in the table */ - TValue k; getnodekey(L, &k, old); + TValue k; + getnodekey(L, &k, old); setobjt2t(L, luaH_set(L, t, &k), gval(old)); } } +} + + +/* +** Resize table 't' for the new given sizes. Both allocations +** (for the hash part and for the array part) can fail, which +** creates some subtleties. If the first allocation, for the hash +** part, fails, an error is raised and that is it. Otherwise, +** copy the elements in the shrinking part of the array (if it +** is shrinking) into the new hash. Then it reallocates the array part. +** If that fails, it frees the new hash part and restores the old hash +** part (to restore the original state of the table), and then raises +** the allocation error. Otherwise, initialize the new part of the +** array (if any) with nils and reinsert the elements in the old +** hash back into the new parts of the table. +*/ +void luaH_resize (lua_State *L, Table *t, unsigned int newasize, + unsigned int nhsize) { + unsigned int i; + Node *oldnode = t->node; /* save old hash ... */ + Node *oldlastfree = t->lastfree; + int oldlsizenode = t->lsizenode; + int oldhsize = allocsizenode(t); + unsigned int oldasize = t->sizearray; + TValue *newarray; + /* create new hash part with appropriate size */ + setnodevector(L, t, nhsize); + if (newasize < oldasize) { /* will array shrink? */ + /* re-insert into the hash the elements from vanishing slice */ + t->sizearray = newasize; /* pretend array has new size */ + for (i = newasize; i < oldasize; i++) { + if (!ttisnil(&t->array[i])) + luaH_setint(L, t, i + 1, &t->array[i]); + } + t->sizearray = oldasize; /* restore current size */ + } + /* allocate new array */ + newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue); + if (newarray == NULL && newasize > 0) { /* allocation failed? */ + if (nhsize > 0) /* not the dummy node? */ + luaM_freearray(L, t->node, allocsizenode(t)); /* release new hash part */ + t->node = oldnode; /* restore original hash part */ + t->lastfree = oldlastfree; + t->lsizenode = oldlsizenode; + lua_assert(!isdummy(t) == (t->node != dummynode)); + luaM_error(L); /* error with array unchanged */ + } + /* allocation ok; initialize new part of the array */ + t->array = newarray; + t->sizearray = newasize; + for (i = oldasize; i < newasize; i++) + setnilvalue(&t->array[i]); + /* re-insert elements from old hash part into new parts */ + reinsert(L, oldnode, oldhsize, t); + /* free old hash */ if (oldhsize > 0) /* not the dummy node? */ - luaM_freearray(L, nold, cast(size_t, oldhsize)); /* free old hash */ + luaM_freearray(L, oldnode, cast(size_t, oldhsize)); + lua_assert(!isdummy(t) == (t->node != dummynode)); }