diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2016-01-04 14:44:50 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2016-01-04 14:44:50 -0200 |
commit | a272fa66f0c149689e2a2c896434967b7248a0eb (patch) | |
tree | 769d3991684ca4c00e1166eea855100b18ba196c | |
parent | b12b635a9052240538daf0d049834b60aa64b435 (diff) | |
download | lua-a272fa66f0c149689e2a2c896434967b7248a0eb.tar.gz lua-a272fa66f0c149689e2a2c896434967b7248a0eb.tar.bz2 lua-a272fa66f0c149689e2a2c896434967b7248a0eb.zip |
bug: Metatable may access its own dealocated field when
it has a self reference in __newindex + some refactoring
-rw-r--r-- | lvm.c | 43 |
1 files changed, 22 insertions, 21 deletions
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lvm.c,v 2.264 2015/11/19 19:16:22 roberto Exp roberto $ | 2 | ** $Id: lvm.c,v 2.265 2015/11/23 11:30:45 roberto Exp roberto $ |
3 | ** Lua virtual machine | 3 | ** Lua virtual machine |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -153,7 +153,7 @@ static int forlimit (const TValue *obj, lua_Integer *p, lua_Integer step, | |||
153 | 153 | ||
154 | 154 | ||
155 | /* | 155 | /* |
156 | ** Complete a table access: if 't' is a table, 'tm' has its metamethod; | 156 | ** Finish a table access: if 't' is a table, 'tm' has its metamethod; |
157 | ** otherwise, 'tm' is NULL. | 157 | ** otherwise, 'tm' is NULL. |
158 | */ | 158 | */ |
159 | void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, | 159 | void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, |
@@ -176,32 +176,33 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, | |||
176 | } | 176 | } |
177 | /* else repeat */ | 177 | /* else repeat */ |
178 | } | 178 | } |
179 | luaG_runerror(L, "gettable chain too long; possible loop"); | 179 | luaG_runerror(L, "'__index' chain too long; possible loop"); |
180 | } | 180 | } |
181 | 181 | ||
182 | 182 | ||
183 | /* | 183 | /* |
184 | ** Main function for table assignment (invoking metamethods if needed). | 184 | ** Finish a table assignment 't[key] = val'. |
185 | ** Compute 't[key] = val' | 185 | ** If 'oldval' is NULL, 't' is not a table. Otherwise, 'oldval' points |
186 | ** to the entry 't[key]', or to 'luaO_nilobject' if there is no such | ||
187 | ** entry. (The value at 'oldval' must be nil, otherwise 'luaV_fastset' | ||
188 | ** would have done the job.) | ||
186 | */ | 189 | */ |
187 | void luaV_finishset (lua_State *L, const TValue *t, TValue *key, | 190 | void luaV_finishset (lua_State *L, const TValue *t, TValue *key, |
188 | StkId val, const TValue *oldval) { | 191 | StkId val, const TValue *oldval) { |
189 | int loop; /* counter to avoid infinite loops */ | 192 | int loop; /* counter to avoid infinite loops */ |
190 | for (loop = 0; loop < MAXTAGLOOP; loop++) { | 193 | for (loop = 0; loop < MAXTAGLOOP; loop++) { |
191 | const TValue *tm; | 194 | const TValue *tm; /* '__newindex' metamethod */ |
192 | if (oldval != NULL) { | 195 | if (oldval != NULL) { /* is 't' a table? */ |
193 | lua_assert(ttistable(t) && ttisnil(oldval)); | 196 | Table *h = hvalue(t); /* save 't' table */ |
194 | /* must check the metamethod */ | 197 | lua_assert(ttisnil(oldval)); /* old value must be nil */ |
195 | if ((tm = fasttm(L, hvalue(t)->metatable, TM_NEWINDEX)) == NULL && | 198 | tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ |
196 | /* no metamethod; is there a previous entry in the table? */ | 199 | if (tm == NULL) { /* no metamethod? */ |
197 | (oldval != luaO_nilobject || | 200 | if (oldval == luaO_nilobject) /* no previous entry? */ |
198 | /* no previous entry; must create one. (The next test is | 201 | oldval = luaH_newkey(L, h, key); /* create one */ |
199 | always true; we only need the assignment.) */ | ||
200 | (oldval = luaH_newkey(L, hvalue(t), key), 1))) { | ||
201 | /* no metamethod and (now) there is an entry with given key */ | 202 | /* no metamethod and (now) there is an entry with given key */ |
202 | setobj2t(L, cast(TValue *, oldval), val); | 203 | setobj2t(L, cast(TValue *, oldval), val); /* set its new value */ |
203 | invalidateTMcache(hvalue(t)); | 204 | invalidateTMcache(h); |
204 | luaC_barrierback(L, hvalue(t), val); | 205 | luaC_barrierback(L, h, val); |
205 | return; | 206 | return; |
206 | } | 207 | } |
207 | /* else will try the metamethod */ | 208 | /* else will try the metamethod */ |
@@ -220,7 +221,7 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, | |||
220 | return; /* done */ | 221 | return; /* done */ |
221 | /* else loop */ | 222 | /* else loop */ |
222 | } | 223 | } |
223 | luaG_runerror(L, "settable chain too long; possible loop"); | 224 | luaG_runerror(L, "'__newindex' chain too long; possible loop"); |
224 | } | 225 | } |
225 | 226 | ||
226 | 227 | ||
@@ -744,8 +745,8 @@ void luaV_finishOp (lua_State *L) { | |||
744 | 745 | ||
745 | 746 | ||
746 | /* | 747 | /* |
747 | ** copy of 'luaV_gettable', but protecting call to potential metamethod | 748 | ** copy of 'luaV_gettable', but protecting the call to potential |
748 | ** (which can reallocate the stack) | 749 | ** metamethod (which can reallocate the stack) |
749 | */ | 750 | */ |
750 | #define gettableProtected(L,t,k,v) { const TValue *aux; \ | 751 | #define gettableProtected(L,t,k,v) { const TValue *aux; \ |
751 | if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ | 752 | if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ |