diff --git a/bugs b/bugs index 8761cb63..f3caeec2 100644 --- a/bugs +++ b/bugs @@ -3680,9 +3680,9 @@ It needs an "interceptor" 'memcmp' function that continues reading memory after a difference is found.]], patch = [[ 2c2 -< ** $Id: bugs,v 1.155 2017/07/27 13:55:38 roberto Exp roberto $ +< ** $Id: bugs,v 1.156 2017/08/12 13:12:42 roberto Exp roberto $ --- -> ** $Id: bugs,v 1.155 2017/07/27 13:55:38 roberto Exp roberto $ +> ** $Id: bugs,v 1.156 2017/08/12 13:12:42 roberto Exp roberto $ 263c263,264 < for (option = LUA_STRFTIMEOPTIONS; *option != '\0'; option += oplen) { --- @@ -3837,6 +3837,40 @@ patch = [[ } +Bug{ +what = [[dead keys with nil values can stay in weak tables]], +report = [[云风 Cloud Wu, 2017/08/15]], +since = [[5.2]], +fix = nil, +example = [[ +-- The following chunk, under a memory checker like valgrind, +-- produces a memory access violation. + +local a = setmetatable({}, {__mode = 'kv'}) + +a['ABCDEFGHIJKLMNOPQRSTUVWXYZ' .. 'abcdefghijklmnopqrstuvwxyz'] = {} +a[next(a)] = nil +collectgarbage() +print(a['BCDEFGHIJKLMNOPQRSTUVWXYZ' .. 'abcdefghijklmnopqrstuvwxyz']) +]], +patch = [[ +--- lgc.c 2016/12/22 13:08:50 2.215 ++++ lgc.c 2017/08/31 16:08:23 +@@ -643,8 +643,9 @@ + for (n = gnode(h, 0); n < limit; n++) { + if (!ttisnil(gval(n)) && (iscleared(g, gkey(n)))) { + setnilvalue(gval(n)); /* remove value ... */ +- removeentry(n); /* and remove entry from table */ + } ++ if (ttisnil(gval(n))) /* is entry empty? */ ++ removeentry(n); /* remove entry from table */ + } + } + } +]] +} + + --[=[ diff --git a/lgc.c b/lgc.c index c39850a4..e658a6f8 100644 --- a/lgc.c +++ b/lgc.c @@ -1,5 +1,5 @@ /* -** $Id: lgc.c,v 2.232 2017/06/12 14:21:44 roberto Exp roberto $ +** $Id: lgc.c,v 2.233 2017/06/29 15:06:44 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -117,7 +117,8 @@ static lu_mem atomic (lua_State *L); /* -** If key is not marked, mark its entry as dead. This allows the +** Clear keys for empty entries in tables. If entry is empty +** and its key is not marked, mark its entry as dead. This allows the ** collection of the key, but keeps its entry in the table (its removal ** could break a chain). Other places never manipulate dead keys, ** because its associated nil value is enough to signal that the entry @@ -688,10 +689,10 @@ static void clearkeys (global_State *g, GCObject *l) { Table *h = gco2t(l); Node *n, *limit = gnodelast(h); for (n = gnode(h, 0); n < limit; n++) { - if (!ttisnil(gval(n)) && (iscleared(g, gckeyN(n)))) { - setnilvalue(gval(n)); /* remove value ... */ - removeentry(n); /* and remove entry from table */ - } + if (!ttisnil(gval(n)) && (iscleared(g, gckeyN(n)))) /* unmarked key? */ + setnilvalue(gval(n)); /* clear value */ + if (ttisnil(gval(n))) /* is entry empty? */ + removeentry(n); /* remove it from table */ } } } @@ -712,10 +713,10 @@ static void clearvalues (global_State *g, GCObject *l, GCObject *f) { setnilvalue(o); /* remove value */ } for (n = gnode(h, 0); n < limit; n++) { - if (iscleared(g, gcvalueN(gval(n)))) { - setnilvalue(gval(n)); /* remove value ... */ - removeentry(n); /* and remove entry from table */ - } + if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */ + setnilvalue(gval(n)); /* clear value */ + if (ttisnil(gval(n))) /* is entry empty? */ + removeentry(n); /* remove it from table */ } } }