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 | ||