diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-07-06 12:09:44 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-07-06 12:09:44 -0300 |
commit | b57574d6fb9071b2f8f261b32c9378ed72db7023 (patch) | |
tree | 41c6503788b696b6056c2d954da84843eebe321e | |
parent | bfcf06d91a87b7ffb8c83e290db0cb6176a167f8 (diff) | |
download | lua-b57574d6fb9071b2f8f261b32c9378ed72db7023.tar.gz lua-b57574d6fb9071b2f8f261b32c9378ed72db7023.tar.bz2 lua-b57574d6fb9071b2f8f261b32c9378ed72db7023.zip |
Keep memory errors as memory errors
Allow memory errors to be raised through the API (throwing the
error with the memory error message); error in external allocations
raises a memory error; memory errors in coroutines are re-raised
as memory errors.
-rw-r--r-- | lapi.c | 8 | ||||
-rw-r--r-- | lauxlib.c | 6 | ||||
-rw-r--r-- | lcorolib.c | 9 | ||||
-rw-r--r-- | testes/api.lua | 75 |
4 files changed, 75 insertions, 23 deletions
@@ -1195,9 +1195,15 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { | |||
1195 | 1195 | ||
1196 | 1196 | ||
1197 | LUA_API int lua_error (lua_State *L) { | 1197 | LUA_API int lua_error (lua_State *L) { |
1198 | TValue *errobj; | ||
1198 | lua_lock(L); | 1199 | lua_lock(L); |
1200 | errobj = s2v(L->top - 1); | ||
1199 | api_checknelems(L, 1); | 1201 | api_checknelems(L, 1); |
1200 | luaG_errormsg(L); | 1202 | /* error object is the memory error message? */ |
1203 | if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg)) | ||
1204 | luaM_error(L); /* raise a memory error */ | ||
1205 | else | ||
1206 | luaG_errormsg(L); /* raise a regular error */ | ||
1201 | /* code unreachable; will unlock when control actually leaves the kernel */ | 1207 | /* code unreachable; will unlock when control actually leaves the kernel */ |
1202 | return 0; /* to avoid warnings */ | 1208 | return 0; /* to avoid warnings */ |
1203 | } | 1209 | } |
@@ -475,8 +475,10 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) { | |||
475 | lua_Alloc allocf = lua_getallocf(L, &ud); | 475 | lua_Alloc allocf = lua_getallocf(L, &ud); |
476 | UBox *box = (UBox *)lua_touserdata(L, idx); | 476 | UBox *box = (UBox *)lua_touserdata(L, idx); |
477 | void *temp = allocf(ud, box->box, box->bsize, newsize); | 477 | void *temp = allocf(ud, box->box, box->bsize, newsize); |
478 | if (temp == NULL && newsize > 0) /* allocation error? */ | 478 | if (temp == NULL && newsize > 0) { /* allocation error? */ |
479 | luaL_error(L, "not enough memory"); | 479 | lua_pushliteral(L, "not enough memory"); |
480 | lua_error(L); /* raise a memory error */ | ||
481 | } | ||
480 | box->box = temp; | 482 | box->box = temp; |
481 | box->bsize = newsize; | 483 | box->bsize = newsize; |
482 | return temp; | 484 | return temp; |
@@ -73,11 +73,12 @@ static int luaB_coresume (lua_State *L) { | |||
73 | static int luaB_auxwrap (lua_State *L) { | 73 | static int luaB_auxwrap (lua_State *L) { |
74 | lua_State *co = lua_tothread(L, lua_upvalueindex(1)); | 74 | lua_State *co = lua_tothread(L, lua_upvalueindex(1)); |
75 | int r = auxresume(L, co, lua_gettop(L)); | 75 | int r = auxresume(L, co, lua_gettop(L)); |
76 | if (r < 0) { | 76 | if (r < 0) { /* error? */ |
77 | int stat = lua_status(co); | 77 | int stat = lua_status(co); |
78 | if (stat != LUA_OK && stat != LUA_YIELD) | 78 | if (stat != LUA_OK && stat != LUA_YIELD) /* error in the coroutine? */ |
79 | lua_resetthread(co); /* close variables in case of errors */ | 79 | lua_resetthread(co); /* close its tbc variables */ |
80 | if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */ | 80 | if (stat != LUA_ERRMEM && /* not a memory error and ... */ |
81 | lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ | ||
81 | luaL_where(L, 1); /* add extra info, if available */ | 82 | luaL_where(L, 1); /* add extra info, if available */ |
82 | lua_insert(L, -2); | 83 | lua_insert(L, -2); |
83 | lua_concat(L, 2); | 84 | lua_concat(L, 2); |
diff --git a/testes/api.lua b/testes/api.lua index 9447e42a..95551481 100644 --- a/testes/api.lua +++ b/testes/api.lua | |||
@@ -11,6 +11,9 @@ local debug = require "debug" | |||
11 | local pack = table.pack | 11 | local pack = table.pack |
12 | 12 | ||
13 | 13 | ||
14 | -- standard error message for memory errors | ||
15 | local MEMERRMSG = "not enough memory" | ||
16 | |||
14 | function tcheck (t1, t2) | 17 | function tcheck (t1, t2) |
15 | assert(t1.n == (t2.n or #t2) + 1) | 18 | assert(t1.n == (t2.n or #t2) + 1) |
16 | for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end | 19 | for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end |
@@ -408,7 +411,7 @@ do | |||
408 | 411 | ||
409 | -- memory error | 412 | -- memory error |
410 | T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k) | 413 | T.totalmem(T.totalmem()+10000) -- set low memory limit (+10k) |
411 | assert(T.checkpanic("newuserdata 20000") == "not enough memory") | 414 | assert(T.checkpanic("newuserdata 20000") == MEMERRMSG) |
412 | T.totalmem(0) -- restore high limit | 415 | T.totalmem(0) -- restore high limit |
413 | 416 | ||
414 | -- stack error | 417 | -- stack error |
@@ -1153,40 +1156,74 @@ do | |||
1153 | end | 1156 | end |
1154 | 1157 | ||
1155 | 1158 | ||
1156 | ------------------------------------------------------------------------- | 1159 | --[[ |
1157 | -- testing memory limits | 1160 | ** {================================================================== |
1158 | ------------------------------------------------------------------------- | 1161 | ** Testing memory limits |
1162 | ** =================================================================== | ||
1163 | --]] | ||
1164 | |||
1159 | print("memory-allocation errors") | 1165 | print("memory-allocation errors") |
1160 | 1166 | ||
1161 | checkerr("block too big", T.newuserdata, math.maxinteger) | 1167 | checkerr("block too big", T.newuserdata, math.maxinteger) |
1162 | collectgarbage() | 1168 | collectgarbage() |
1163 | local f = load"local a={}; for i=1,100000 do a[i]=i end" | 1169 | local f = load"local a={}; for i=1,100000 do a[i]=i end" |
1164 | T.alloccount(10) | 1170 | T.alloccount(10) |
1165 | checkerr("not enough memory", f) | 1171 | checkerr(MEMERRMSG, f) |
1166 | T.alloccount() -- remove limit | 1172 | T.alloccount() -- remove limit |
1167 | 1173 | ||
1174 | |||
1175 | -- test memory errors; increase limit for maximum memory by steps, | ||
1176 | -- o that we get memory errors in all allocations of a given | ||
1177 | -- task, until there is enough memory to complete the task without | ||
1178 | -- errors. | ||
1179 | function testbytes (s, f) | ||
1180 | collectgarbage() | ||
1181 | local M = T.totalmem() | ||
1182 | local oldM = M | ||
1183 | local a,b = nil | ||
1184 | while true do | ||
1185 | collectgarbage(); collectgarbage() | ||
1186 | T.totalmem(M) | ||
1187 | a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f) | ||
1188 | T.totalmem(0) -- remove limit | ||
1189 | if a and b == "OK" then break end -- stop when no more errors | ||
1190 | if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error? | ||
1191 | error(a, 0) -- propagate it | ||
1192 | end | ||
1193 | M = M + 7 -- increase memory limit | ||
1194 | end | ||
1195 | print(string.format("minimum memory for %s: %d bytes", s, M - oldM)) | ||
1196 | return a | ||
1197 | end | ||
1198 | |||
1168 | -- test memory errors; increase limit for number of allocations one | 1199 | -- test memory errors; increase limit for number of allocations one |
1169 | -- by one, so that we get memory errors in all allocations of a given | 1200 | -- by one, so that we get memory errors in all allocations of a given |
1170 | -- task, until there is enough allocations to complete the task without | 1201 | -- task, until there is enough allocations to complete the task without |
1171 | -- errors. | 1202 | -- errors. |
1172 | 1203 | ||
1173 | function testamem (s, f) | 1204 | function testalloc (s, f) |
1174 | collectgarbage(); collectgarbage() | 1205 | collectgarbage() |
1175 | local M = 0 | 1206 | local M = 0 |
1176 | local a,b = nil | 1207 | local a,b = nil |
1177 | while true do | 1208 | while true do |
1209 | collectgarbage(); collectgarbage() | ||
1178 | T.alloccount(M) | 1210 | T.alloccount(M) |
1179 | a, b = pcall(f) | 1211 | a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f) |
1180 | T.alloccount() -- remove limit | 1212 | T.alloccount() -- remove limit |
1181 | if a and b then break end -- stop when no more errors | 1213 | if a and b == "OK" then break end -- stop when no more errors |
1182 | if not a and not -- `real' error? | 1214 | if b ~= "OK" and b ~= MEMERRMSG then -- not a memory error? |
1183 | (string.find(b, "memory") or string.find(b, "overflow")) then | 1215 | error(a, 0) -- propagate it |
1184 | error(b, 0) -- propagate it | ||
1185 | end | 1216 | end |
1186 | M = M + 1 -- increase allocation limit | 1217 | M = M + 1 -- increase allocation limit |
1187 | end | 1218 | end |
1188 | print(string.format("limit for %s: %d allocations", s, M)) | 1219 | print(string.format("minimum allocations for %s: %d allocations", s, M)) |
1189 | return b | 1220 | return a |
1221 | end | ||
1222 | |||
1223 | |||
1224 | local function testamem (s, f) | ||
1225 | testalloc(s, f) | ||
1226 | return testbytes(s, f) | ||
1190 | end | 1227 | end |
1191 | 1228 | ||
1192 | 1229 | ||
@@ -1196,8 +1233,11 @@ assert(b == 10) | |||
1196 | 1233 | ||
1197 | -- testing memory errors when creating a new state | 1234 | -- testing memory errors when creating a new state |
1198 | 1235 | ||
1199 | b = testamem("state creation", T.newstate) | 1236 | testamem("state creation", function () |
1200 | T.closestate(b); -- close new state | 1237 | local st = T.newstate() |
1238 | if st then T.closestate(st) end -- close new state | ||
1239 | return st | ||
1240 | end) | ||
1201 | 1241 | ||
1202 | testamem("empty-table creation", function () | 1242 | testamem("empty-table creation", function () |
1203 | return {} | 1243 | return {} |
@@ -1345,6 +1385,9 @@ testamem("growing stack", function () | |||
1345 | return foo(100) | 1385 | return foo(100) |
1346 | end) | 1386 | end) |
1347 | 1387 | ||
1388 | -- }================================================================== | ||
1389 | |||
1390 | |||
1348 | do -- testing failing in 'lua_checkstack' | 1391 | do -- testing failing in 'lua_checkstack' |
1349 | local res = T.testC([[rawcheckstack 500000; return 1]]) | 1392 | local res = T.testC([[rawcheckstack 500000; return 1]]) |
1350 | assert(res == false) | 1393 | assert(res == false) |