aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ldblib.c12
-rw-r--r--ldo.c7
-rw-r--r--lstate.c28
-rw-r--r--lstate.h5
-rw-r--r--ltests.h2
-rw-r--r--lua.h1
-rw-r--r--manual/manual.of42
-rw-r--r--testes/cstack.lua48
-rw-r--r--testes/errors.lua16
9 files changed, 149 insertions, 12 deletions
diff --git a/ldblib.c b/ldblib.c
index d045a82e..513a13cb 100644
--- a/ldblib.c
+++ b/ldblib.c
@@ -437,6 +437,17 @@ static int db_traceback (lua_State *L) {
437} 437}
438 438
439 439
440static 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
440static const luaL_Reg dblib[] = { 451static 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
diff --git a/ldo.c b/ldo.c
index 0ad3120b..1a327ffd 100644
--- a/ldo.c
+++ b/ldo.c
@@ -139,7 +139,8 @@ l_noret luaD_throw (lua_State *L, int errcode) {
139 139
140 140
141int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { 141int 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)
diff --git a/lstate.c b/lstate.c
index 296cec2a..92d5165a 100644
--- a/lstate.c
+++ b/lstate.c
@@ -96,6 +96,29 @@ void luaE_setdebt (global_State *g, l_mem debt) {
96} 96}
97 97
98 98
99LUA_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;
diff --git a/lstate.h b/lstate.h
index 858da5be..d3a64f94 100644
--- a/lstate.h
+++ b/lstate.h
@@ -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
diff --git a/ltests.h b/ltests.h
index a22c98e1..0ae6afc4 100644
--- a/ltests.h
+++ b/ltests.h
@@ -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))
diff --git a/lua.h b/lua.h
index ec31c781..d3575fd9 100644
--- a/lua.h
+++ b/lua.h
@@ -462,6 +462,7 @@ LUA_API lua_Hook (lua_gethook) (lua_State *L);
462LUA_API int (lua_gethookmask) (lua_State *L); 462LUA_API int (lua_gethookmask) (lua_State *L);
463LUA_API int (lua_gethookcount) (lua_State *L); 463LUA_API int (lua_gethookcount) (lua_State *L);
464 464
465LUA_API int (lua_setCstacklimit) (lua_State *L, unsigned int limit);
465 466
466struct lua_Debug { 467struct 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
4809Sets a new limit for the C stack.
4810This limit controls how deeply nested calls can go in Lua,
4811with the intent of avoiding a stack overflow.
4812Returns the old limit in case of success,
4813or zero in case of error.
4814For more details about this function,
4815see @Lid{debug.setCstacklimit},
4816its 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
8535Sets a new limit for the C stack.
8536This limit controls how deeply nested calls can go in Lua,
8537with the intent of avoiding a stack overflow.
8538A limit too small restricts recursive calls pointlessly;
8539a limit too large exposes the interpreter to stack-overflow crashes.
8540Unfortunately, there is no way to know a priori
8541the maximum safe limit for a platform.
8542
8543Each call made from Lua code counts one unit.
8544Other operations (e.g., calls made from C to Lua or resuming a coroutine)
8545may have a higher cost.
8546
8547This 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}
8554In case of success,
8555this function returns the old limit.
8556In case of error,
8557it returns @false.
8558
8559}
8560
8519@LibEntry{debug.sethook ([thread,] hook, mask [, count])| 8561@LibEntry{debug.sethook ([thread,] hook, mask [, count])|
8520 8562
8521Sets the given function as the debug hook. 8563Sets 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
4local debug = require "debug"
5
4print"testing C-stack overflow detection" 6print"testing C-stack overflow detection"
5 7
8local origlimit = debug.setCstacklimit(400)
9print("current stack limit: " .. origlimit)
10debug.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)
80end 86end
81 87
88
89do 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
127end
128
129
82print'OK' 130print'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
526local function testrep (init, rep, close, repc) 526local 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
535testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment 539testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment
536testrep("local a; a=", "{", "0", "}") 540testrep("local a; a=", "{", "0", "}")
537testrep("local a; a=", "(", "2", ")") 541testrep("return ", "(", "2", ")", 2)
538testrep("local a; ", "a(", "2", ")") 542testrep("local function a (x) return x end; return ", "a(", "2.2", ")", 2.2)
539testrep("", "do ", "", " end") 543testrep("", "do ", "", " end")
540testrep("", "while a do ", "", " end") 544testrep("", "while a do ", "", " end")
541testrep("local a; ", "if a then else ", "", " end") 545testrep("local a; ", "if a then else ", "", " end")
542testrep("", "function foo () ", "", " end") 546testrep("", "function foo () ", "", " end")
543testrep("local a; a=", "a..", "a", "") 547testrep("local a = ''; return ", "a..", "'a'", "", "a")
544testrep("local a; a=", "a^", "a", "") 548testrep("local a = 1; return ", "a^", "a", "", 1)
545 549
546checkmessage("a = f(x" .. string.rep(",x", 260) .. ")", "too many registers") 550checkmessage("a = f(x" .. string.rep(",x", 260) .. ")", "too many registers")
547 551