diff options
| -rw-r--r-- | ldblib.c | 12 | ||||
| -rw-r--r-- | ldo.c | 7 | ||||
| -rw-r--r-- | lstate.c | 28 | ||||
| -rw-r--r-- | lstate.h | 5 | ||||
| -rw-r--r-- | ltests.h | 2 | ||||
| -rw-r--r-- | lua.h | 1 | ||||
| -rw-r--r-- | manual/manual.of | 42 | ||||
| -rw-r--r-- | testes/cstack.lua | 48 | ||||
| -rw-r--r-- | testes/errors.lua | 16 |
9 files changed, 149 insertions, 12 deletions
| @@ -437,6 +437,17 @@ static int db_traceback (lua_State *L) { | |||
| 437 | } | 437 | } |
| 438 | 438 | ||
| 439 | 439 | ||
| 440 | static int db_setCstacklimit (lua_State *L) { | ||
| 441 | int limit = (int)luaL_checkinteger(L, 1); | ||
| 442 | int res = lua_setCstacklimit(L, limit); | ||
| 443 | if (res == 0) | ||
| 444 | lua_pushboolean(L, 0); | ||
| 445 | else | ||
| 446 | lua_pushinteger(L, res); | ||
| 447 | return 1; | ||
| 448 | } | ||
| 449 | |||
| 450 | |||
| 440 | static const luaL_Reg dblib[] = { | 451 | static const luaL_Reg dblib[] = { |
| 441 | {"debug", db_debug}, | 452 | {"debug", db_debug}, |
| 442 | {"getuservalue", db_getuservalue}, | 453 | {"getuservalue", db_getuservalue}, |
| @@ -454,6 +465,7 @@ static const luaL_Reg dblib[] = { | |||
| 454 | {"setmetatable", db_setmetatable}, | 465 | {"setmetatable", db_setmetatable}, |
| 455 | {"setupvalue", db_setupvalue}, | 466 | {"setupvalue", db_setupvalue}, |
| 456 | {"traceback", db_traceback}, | 467 | {"traceback", db_traceback}, |
| 468 | {"setCstacklimit", db_setCstacklimit}, | ||
| 457 | {NULL, NULL} | 469 | {NULL, NULL} |
| 458 | }; | 470 | }; |
| 459 | 471 | ||
| @@ -139,7 +139,8 @@ l_noret luaD_throw (lua_State *L, int errcode) { | |||
| 139 | 139 | ||
| 140 | 140 | ||
| 141 | int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { | 141 | int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { |
| 142 | l_uint32 oldnCcalls = L->nCcalls + L->nci; | 142 | global_State *g = G(L); |
| 143 | l_uint32 oldnCcalls = g->Cstacklimit - (L->nCcalls + L->nci); | ||
| 143 | struct lua_longjmp lj; | 144 | struct lua_longjmp lj; |
| 144 | lj.status = LUA_OK; | 145 | lj.status = LUA_OK; |
| 145 | lj.previous = L->errorJmp; /* chain new error handler */ | 146 | lj.previous = L->errorJmp; /* chain new error handler */ |
| @@ -148,7 +149,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { | |||
| 148 | (*f)(L, ud); | 149 | (*f)(L, ud); |
| 149 | ); | 150 | ); |
| 150 | L->errorJmp = lj.previous; /* restore old error handler */ | 151 | L->errorJmp = lj.previous; /* restore old error handler */ |
| 151 | L->nCcalls = oldnCcalls - L->nci; | 152 | L->nCcalls = g->Cstacklimit - oldnCcalls - L->nci; |
| 152 | return lj.status; | 153 | return lj.status; |
| 153 | } | 154 | } |
| 154 | 155 | ||
| @@ -671,7 +672,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, | |||
| 671 | else if (L->status != LUA_YIELD) /* ended with errors? */ | 672 | else if (L->status != LUA_YIELD) /* ended with errors? */ |
| 672 | return resume_error(L, "cannot resume dead coroutine", nargs); | 673 | return resume_error(L, "cannot resume dead coroutine", nargs); |
| 673 | if (from == NULL) | 674 | if (from == NULL) |
| 674 | L->nCcalls = LUAI_MAXCSTACK; | 675 | L->nCcalls = CSTACKTHREAD; |
| 675 | else /* correct 'nCcalls' for this thread */ | 676 | else /* correct 'nCcalls' for this thread */ |
| 676 | L->nCcalls = getCcalls(from) + from->nci - L->nci - CSTACKCF; | 677 | L->nCcalls = getCcalls(from) + from->nci - L->nci - CSTACKCF; |
| 677 | if (L->nCcalls <= CSTACKERR) | 678 | if (L->nCcalls <= CSTACKERR) |
| @@ -96,6 +96,29 @@ void luaE_setdebt (global_State *g, l_mem debt) { | |||
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | 98 | ||
| 99 | LUA_API int lua_setCstacklimit (lua_State *L, unsigned int limit) { | ||
| 100 | global_State *g = G(L); | ||
| 101 | int ccalls; | ||
| 102 | luaE_freeCI(L); /* release unused CIs */ | ||
| 103 | ccalls = getCcalls(L); | ||
| 104 | if (limit >= 40000) | ||
| 105 | return 0; /* out of bounds */ | ||
| 106 | limit += CSTACKERR; | ||
| 107 | if (L != g-> mainthread) | ||
| 108 | return 0; /* only main thread can change the C stack */ | ||
| 109 | else if (ccalls <= CSTACKERR) | ||
| 110 | return 0; /* handling overflow */ | ||
| 111 | else { | ||
| 112 | int diff = limit - g->Cstacklimit; | ||
| 113 | if (ccalls + diff <= CSTACKERR) | ||
| 114 | return 0; /* new limit would cause an overflow */ | ||
| 115 | g->Cstacklimit = limit; /* set new limit */ | ||
| 116 | L->nCcalls += diff; /* correct 'nCcalls' */ | ||
| 117 | return limit - diff - CSTACKERR; /* success; return previous limit */ | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | |||
| 99 | /* | 122 | /* |
| 100 | ** Decrement count of "C calls" and check for overflows. In case of | 123 | ** Decrement count of "C calls" and check for overflows. In case of |
| 101 | ** a stack overflow, check appropriate error ("regular" overflow or | 124 | ** a stack overflow, check appropriate error ("regular" overflow or |
| @@ -121,7 +144,7 @@ void luaE_enterCcall (lua_State *L) { | |||
| 121 | else if (ncalls >= CSTACKMARK) { | 144 | else if (ncalls >= CSTACKMARK) { |
| 122 | /* not in error-handling zone; raise the error now */ | 145 | /* not in error-handling zone; raise the error now */ |
| 123 | L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */ | 146 | L->nCcalls = (CSTACKMARK - 1); /* enter error-handling zone */ |
| 124 | luaG_runerror(L, "C stack overflow1"); | 147 | luaG_runerror(L, "C stack overflow"); |
| 125 | } | 148 | } |
| 126 | /* else stack is in the error-handling zone; | 149 | /* else stack is in the error-handling zone; |
| 127 | allow message handler to work */ | 150 | allow message handler to work */ |
| @@ -263,7 +286,7 @@ static void preinit_thread (lua_State *L, global_State *g) { | |||
| 263 | L->stacksize = 0; | 286 | L->stacksize = 0; |
| 264 | L->twups = L; /* thread has no upvalues */ | 287 | L->twups = L; /* thread has no upvalues */ |
| 265 | L->errorJmp = NULL; | 288 | L->errorJmp = NULL; |
| 266 | L->nCcalls = LUAI_MAXCSTACK + CSTACKERR; | 289 | L->nCcalls = CSTACKTHREAD; |
| 267 | L->hook = NULL; | 290 | L->hook = NULL; |
| 268 | L->hookmask = 0; | 291 | L->hookmask = 0; |
| 269 | L->basehookcount = 0; | 292 | L->basehookcount = 0; |
| @@ -365,6 +388,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { | |||
| 365 | preinit_thread(L, g); | 388 | preinit_thread(L, g); |
| 366 | g->allgc = obj2gco(L); /* by now, only object is the main thread */ | 389 | g->allgc = obj2gco(L); /* by now, only object is the main thread */ |
| 367 | L->next = NULL; | 390 | L->next = NULL; |
| 391 | g->Cstacklimit = L->nCcalls = LUAI_MAXCSTACK; | ||
| 368 | g->frealloc = f; | 392 | g->frealloc = f; |
| 369 | g->ud = ud; | 393 | g->ud = ud; |
| 370 | g->warnf = NULL; | 394 | g->warnf = NULL; |
| @@ -103,6 +103,10 @@ | |||
| 103 | #define CSTACKERRMARK (CSTACKCF + 2) | 103 | #define CSTACKERRMARK (CSTACKCF + 2) |
| 104 | 104 | ||
| 105 | 105 | ||
| 106 | /* initial limit for the C-stack of threads */ | ||
| 107 | #define CSTACKTHREAD (2 * CSTACKERR) | ||
| 108 | |||
| 109 | |||
| 106 | /* true if this thread does not have non-yieldable calls in the stack */ | 110 | /* true if this thread does not have non-yieldable calls in the stack */ |
| 107 | #define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) | 111 | #define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) |
| 108 | 112 | ||
| @@ -267,6 +271,7 @@ typedef struct global_State { | |||
| 267 | TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ | 271 | TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ |
| 268 | lua_WarnFunction warnf; /* warning function */ | 272 | lua_WarnFunction warnf; /* warning function */ |
| 269 | void *ud_warn; /* auxiliary data to 'warnf' */ | 273 | void *ud_warn; /* auxiliary data to 'warnf' */ |
| 274 | unsigned int Cstacklimit; /* current limit for the C stack */ | ||
| 270 | } global_State; | 275 | } global_State; |
| 271 | 276 | ||
| 272 | 277 | ||
| @@ -31,7 +31,7 @@ | |||
| 31 | 31 | ||
| 32 | /* compiled with -O0, Lua uses a lot of C stack space... */ | 32 | /* compiled with -O0, Lua uses a lot of C stack space... */ |
| 33 | #undef LUAI_MAXCSTACK | 33 | #undef LUAI_MAXCSTACK |
| 34 | #define LUAI_MAXCSTACK 400 | 34 | #define LUAI_MAXCSTACK (400 + CSTACKERR) |
| 35 | 35 | ||
| 36 | /* to avoid warnings, and to make sure value is really unused */ | 36 | /* to avoid warnings, and to make sure value is really unused */ |
| 37 | #define UNUSED(x) (x=0, (void)(x)) | 37 | #define UNUSED(x) (x=0, (void)(x)) |
| @@ -462,6 +462,7 @@ LUA_API lua_Hook (lua_gethook) (lua_State *L); | |||
| 462 | LUA_API int (lua_gethookmask) (lua_State *L); | 462 | LUA_API int (lua_gethookmask) (lua_State *L); |
| 463 | LUA_API int (lua_gethookcount) (lua_State *L); | 463 | LUA_API int (lua_gethookcount) (lua_State *L); |
| 464 | 464 | ||
| 465 | LUA_API int (lua_setCstacklimit) (lua_State *L, unsigned int limit); | ||
| 465 | 466 | ||
| 466 | struct lua_Debug { | 467 | struct lua_Debug { |
| 467 | int event; | 468 | int event; |
diff --git a/manual/manual.of b/manual/manual.of index 725b12ad..e9416956 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
| @@ -4803,6 +4803,20 @@ calling @Lid{lua_yield} with @id{nresults} equal to zero | |||
| 4803 | 4803 | ||
| 4804 | } | 4804 | } |
| 4805 | 4805 | ||
| 4806 | @APIEntry{int (lua_setCstacklimit) (lua_State *L, unsigned int limit);| | ||
| 4807 | @apii{0,0,-} | ||
| 4808 | |||
| 4809 | Sets a new limit for the C stack. | ||
| 4810 | This limit controls how deeply nested calls can go in Lua, | ||
| 4811 | with the intent of avoiding a stack overflow. | ||
| 4812 | Returns the old limit in case of success, | ||
| 4813 | or zero in case of error. | ||
| 4814 | For more details about this function, | ||
| 4815 | see @Lid{debug.setCstacklimit}, | ||
| 4816 | its equivalent in the standard library. | ||
| 4817 | |||
| 4818 | } | ||
| 4819 | |||
| 4806 | @APIEntry{void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);| | 4820 | @APIEntry{void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);| |
| 4807 | @apii{0,0,-} | 4821 | @apii{0,0,-} |
| 4808 | 4822 | ||
| @@ -8516,6 +8530,34 @@ to the userdata @id{u} plus a boolean, | |||
| 8516 | 8530 | ||
| 8517 | } | 8531 | } |
| 8518 | 8532 | ||
| 8533 | @LibEntry{debug.setCstacklimit (limit)| | ||
| 8534 | |||
| 8535 | Sets a new limit for the C stack. | ||
| 8536 | This limit controls how deeply nested calls can go in Lua, | ||
| 8537 | with the intent of avoiding a stack overflow. | ||
| 8538 | A limit too small restricts recursive calls pointlessly; | ||
| 8539 | a limit too large exposes the interpreter to stack-overflow crashes. | ||
| 8540 | Unfortunately, there is no way to know a priori | ||
| 8541 | the maximum safe limit for a platform. | ||
| 8542 | |||
| 8543 | Each call made from Lua code counts one unit. | ||
| 8544 | Other operations (e.g., calls made from C to Lua or resuming a coroutine) | ||
| 8545 | may have a higher cost. | ||
| 8546 | |||
| 8547 | This function has the following restrictions: | ||
| 8548 | @description{ | ||
| 8549 | @item{It can only be called from the main coroutine (thread);} | ||
| 8550 | @item{It cannot be called while handling a stack-overflow error;} | ||
| 8551 | @item{@id{limit} must be less than 40000;} | ||
| 8552 | @item{@id{limit} cannot be less than the amount of C stack in use.} | ||
| 8553 | } | ||
| 8554 | In case of success, | ||
| 8555 | this function returns the old limit. | ||
| 8556 | In case of error, | ||
| 8557 | it returns @false. | ||
| 8558 | |||
| 8559 | } | ||
| 8560 | |||
| 8519 | @LibEntry{debug.sethook ([thread,] hook, mask [, count])| | 8561 | @LibEntry{debug.sethook ([thread,] hook, mask [, count])| |
| 8520 | 8562 | ||
| 8521 | Sets the given function as the debug hook. | 8563 | Sets the given function as the debug hook. |
diff --git a/testes/cstack.lua b/testes/cstack.lua index 3cb1e4ce..c7aa740f 100644 --- a/testes/cstack.lua +++ b/testes/cstack.lua | |||
| @@ -1,8 +1,14 @@ | |||
| 1 | -- $Id: testes/cstack.lua $ | 1 | -- $Id: testes/cstack.lua $ |
| 2 | -- See Copyright Notice in file all.lua | 2 | -- See Copyright Notice in file all.lua |
| 3 | 3 | ||
| 4 | local debug = require "debug" | ||
| 5 | |||
| 4 | print"testing C-stack overflow detection" | 6 | print"testing C-stack overflow detection" |
| 5 | 7 | ||
| 8 | local origlimit = debug.setCstacklimit(400) | ||
| 9 | print("current stack limit: " .. origlimit) | ||
| 10 | debug.setCstacklimit(origlimit) | ||
| 11 | |||
| 6 | -- Segmentation faults in these tests probably result from a C-stack | 12 | -- Segmentation faults in these tests probably result from a C-stack |
| 7 | -- overflow. To avoid these errors, recompile Lua with a smaller | 13 | -- overflow. To avoid these errors, recompile Lua with a smaller |
| 8 | -- value for the constant 'LUAI_MAXCCALLS' or else ensure a larger | 14 | -- value for the constant 'LUAI_MAXCCALLS' or else ensure a larger |
| @@ -79,4 +85,46 @@ do print("testing stack-overflow in recursive 'gsub'") | |||
| 79 | print("\tfinal count: ", count) | 85 | print("\tfinal count: ", count) |
| 80 | end | 86 | end |
| 81 | 87 | ||
| 88 | |||
| 89 | do print("testing changes in C-stack limit") | ||
| 90 | |||
| 91 | assert(not debug.setCstacklimit(0)) -- limit too small | ||
| 92 | assert(not debug.setCstacklimit(50000)) -- limit too large | ||
| 93 | local co = coroutine.wrap (function () | ||
| 94 | return debug.setCstacklimit(400) | ||
| 95 | end) | ||
| 96 | assert(co() == false) -- cannot change C stack inside coroutine | ||
| 97 | |||
| 98 | local n | ||
| 99 | local function foo () n = n + 1; foo () end | ||
| 100 | |||
| 101 | local function check () | ||
| 102 | n = 0 | ||
| 103 | pcall(foo) | ||
| 104 | return n | ||
| 105 | end | ||
| 106 | |||
| 107 | assert(debug.setCstacklimit(400) == origlimit) | ||
| 108 | local lim400 = check() | ||
| 109 | -- a very low limit (given that the several calls to arive here) | ||
| 110 | local lowlimit = 38 | ||
| 111 | assert(debug.setCstacklimit(lowlimit) == 400) | ||
| 112 | assert(check() < lowlimit - 30) | ||
| 113 | assert(debug.setCstacklimit(600) == lowlimit) | ||
| 114 | local lim600 = check() | ||
| 115 | assert(lim600 == lim400 + 200) | ||
| 116 | |||
| 117 | |||
| 118 | -- 'setCstacklimit' works inside protected calls. (The new stack | ||
| 119 | -- limit is kept when 'pcall' returns.) | ||
| 120 | assert(pcall(function () | ||
| 121 | assert(debug.setCstacklimit(400) == 600) | ||
| 122 | assert(check() <= lim400) | ||
| 123 | end)) | ||
| 124 | |||
| 125 | assert(check() == lim400) | ||
| 126 | assert(debug.setCstacklimit(origlimit) == 400) -- restore original limit | ||
| 127 | end | ||
| 128 | |||
| 129 | |||
| 82 | print'OK' | 130 | print'OK' |
diff --git a/testes/errors.lua b/testes/errors.lua index 0b12410e..6e7b8004 100644 --- a/testes/errors.lua +++ b/testes/errors.lua | |||
| @@ -523,9 +523,13 @@ end | |||
| 523 | 523 | ||
| 524 | -- testing syntax limits | 524 | -- testing syntax limits |
| 525 | 525 | ||
| 526 | local function testrep (init, rep, close, repc) | 526 | local function testrep (init, rep, close, repc, finalresult) |
| 527 | local s = init .. string.rep(rep, 100) .. close .. string.rep(repc, 100) | 527 | local s = init .. string.rep(rep, 100) .. close .. string.rep(repc, 100) |
| 528 | assert(load(s)) -- 100 levels is OK | 528 | local res, msg = load(s) |
| 529 | assert(res) -- 100 levels is OK | ||
| 530 | if (finalresult) then | ||
| 531 | assert(res() == finalresult) | ||
| 532 | end | ||
| 529 | s = init .. string.rep(rep, 10000) | 533 | s = init .. string.rep(rep, 10000) |
| 530 | local res, msg = load(s) -- 10000 levels not ok | 534 | local res, msg = load(s) -- 10000 levels not ok |
| 531 | assert(not res and (string.find(msg, "too many registers") or | 535 | assert(not res and (string.find(msg, "too many registers") or |
| @@ -534,14 +538,14 @@ end | |||
| 534 | 538 | ||
| 535 | testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment | 539 | testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment |
| 536 | testrep("local a; a=", "{", "0", "}") | 540 | testrep("local a; a=", "{", "0", "}") |
| 537 | testrep("local a; a=", "(", "2", ")") | 541 | testrep("return ", "(", "2", ")", 2) |
| 538 | testrep("local a; ", "a(", "2", ")") | 542 | testrep("local function a (x) return x end; return ", "a(", "2.2", ")", 2.2) |
| 539 | testrep("", "do ", "", " end") | 543 | testrep("", "do ", "", " end") |
| 540 | testrep("", "while a do ", "", " end") | 544 | testrep("", "while a do ", "", " end") |
| 541 | testrep("local a; ", "if a then else ", "", " end") | 545 | testrep("local a; ", "if a then else ", "", " end") |
| 542 | testrep("", "function foo () ", "", " end") | 546 | testrep("", "function foo () ", "", " end") |
| 543 | testrep("local a; a=", "a..", "a", "") | 547 | testrep("local a = ''; return ", "a..", "'a'", "", "a") |
| 544 | testrep("local a; a=", "a^", "a", "") | 548 | testrep("local a = 1; return ", "a^", "a", "", 1) |
| 545 | 549 | ||
| 546 | checkmessage("a = f(x" .. string.rep(",x", 260) .. ")", "too many registers") | 550 | checkmessage("a = f(x" .. string.rep(",x", 260) .. ")", "too many registers") |
| 547 | 551 | ||
