From 9294302fc069e0b8d893521fa552b5ed66601024 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 22 May 2017 17:04:46 +1000 Subject: Fix lua_checkstack to check against LUAI_MAXSTACK. Adds luaD_growstack and luaD_reallocstack --- src/lapi.js | 14 +++++++++++++- src/ldo.js | 27 +++++++++++++++++++++++++++ src/lstate.js | 3 +++ 3 files changed, 43 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/lapi.js b/src/lapi.js index cea0568..79c666c 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -91,8 +91,20 @@ const index2addr_ = function(L, idx) { }; const lua_checkstack = function(L, n) { + let res; let ci = L.ci; - let res = L.stack.length < luaconf.LUAI_MAXSTACK; + assert(n >= 0, "negative 'n'"); + if (L.stack.length - L.top > n) /* stack large enough? */ + res = true; + else { /* no; need to grow stack */ + let inuse = L.top + lstate.EXTRA_STACK; + if (inuse > luaconf.LUAI_MAXSTACK - n) /* can grow without overflow? */ + res = false; /* no */ + else { /* try to grow stack */ + ldo.luaD_growstack(L, n); + res = true; + } + } if (res && ci.top < L.top + n) ci.top = L.top + n; /* adjust frame top */ diff --git a/src/ldo.js b/src/ldo.js index eb8df06..1036ce5 100644 --- a/src/ldo.js +++ b/src/ldo.js @@ -13,6 +13,7 @@ const lparser = require('./lparser.js'); const lstate = require('./lstate.js'); const lstring = require('./lstring.js'); const ltm = require('./ltm.js'); +const luaconf = require('./luaconf.js'); const lundump = require('./lundump.js'); const lvm = require('./lvm.js'); const lzio = require('./lzio.js'); @@ -38,6 +39,30 @@ const seterrorobj = function(L, errcode, oldtop) { L.top = oldtop + 1; }; +const ERRORSTACKSIZE = luaconf.LUAI_MAXSTACK + 200; + +const luaD_reallocstack = function(L, newsize) { + L.stack.length = newsize; +}; + +const luaD_growstack = function(L, n) { + let size = L.stack.length; + if (size > luaconf.LUAI_MAXSTACK) + luaD_throw(L, TS.LUA_ERRERR); + else { + let needed = L.top + n + lstate.EXTRA_STACK; + let newsize = 2 * size; + if (newsize > luaconf.LUAI_MAXSTACK) newsize = luaconf.LUAI_MAXSTACK; + if (newsize < needed) newsize = needed; + if (newsize > luaconf.LUAI_MAXSTACK) { /* stack overflow? */ + luaD_reallocstack(L, ERRORSTACKSIZE); + ldebug.luaG_runerror(L, "stack overflow"); + } + else + luaD_reallocstack(L, newsize); + } +}; + /* ** Prepares a function call: checks the stack, creates a new CallInfo ** entry, fills in the relevant information, calls hook if needed. @@ -594,12 +619,14 @@ module.exports.SParser = SParser; module.exports.adjust_varargs = adjust_varargs; module.exports.luaD_call = luaD_call; module.exports.luaD_callnoyield = luaD_callnoyield; +module.exports.luaD_growstack = luaD_growstack; module.exports.luaD_hook = luaD_hook; module.exports.luaD_pcall = luaD_pcall; module.exports.luaD_poscall = luaD_poscall; module.exports.luaD_precall = luaD_precall; module.exports.luaD_protectedparser = luaD_protectedparser; module.exports.luaD_rawrunprotected = luaD_rawrunprotected; +module.exports.luaD_reallocstack = luaD_reallocstack; module.exports.luaD_throw = luaD_throw; module.exports.lua_isyieldable = lua_isyieldable; module.exports.lua_resume = lua_resume; diff --git a/src/lstate.js b/src/lstate.js index 5a65c12..d1789e8 100644 --- a/src/lstate.js +++ b/src/lstate.js @@ -14,6 +14,8 @@ const CT = defs.constant_types; const TS = defs.thread_status; const LUA_NUMTAGS = defs.LUA_NUMTAGS; +const EXTRA_STACK = 5; + const BASIC_STACK_SIZE = 2 * defs.LUA_MINSTACK; class CallInfo { @@ -200,6 +202,7 @@ module.exports.CIST_TAIL = (1<<5); /* call was tail called */ module.exports.CIST_HOOKYIELD = (1<<6); /* last hook called yielded */ module.exports.CIST_LEQ = (1<<7); /* using __lt for __le */ module.exports.CIST_FIN = (1<<8); /* call is running a finalizer */ +module.exports.EXTRA_STACK = EXTRA_STACK; module.exports.lua_close = lua_close; module.exports.lua_newstate = lua_newstate; module.exports.lua_newthread = lua_newthread; -- cgit v1.2.3-54-g00ecf From 20efa5c3b00e3a82ab135ec4a1f5d0563912aefe Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 22 May 2017 17:48:16 +1000 Subject: Add luaD_checkstack calls --- src/ldo.js | 12 ++++++++++-- src/lobject.js | 2 ++ src/lvm.js | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ldo.js b/src/ldo.js index 1036ce5..5243f1f 100644 --- a/src/ldo.js +++ b/src/ldo.js @@ -63,6 +63,11 @@ const luaD_growstack = function(L, n) { } }; +const luaD_checkstack = function(L, n) { + if (L.stack.length - L.top <= n) + luaD_growstack(L, n); +}; + /* ** Prepares a function call: checks the stack, creates a new CallInfo ** entry, fills in the relevant information, calls hook if needed. @@ -78,11 +83,13 @@ const luaD_precall = function(L, off, nresults) { case CT.LUA_TLCF: { let f = func.type === CT.LUA_TCCL ? func.value.f : func.value; + luaD_checkstack(L, defs.LUA_MINSTACK); let ci = lstate.luaE_extendCI(L); ci.funcOff = off; ci.nresults = nresults; ci.func = func; ci.top = L.top + defs.LUA_MINSTACK; + assert(ci.top <= L.stack.length); ci.callstatus = 0; if (L.hookmask & defs.LUA_MASKCALL) luaD_hook(L, defs.LUA_HOOKCALL, -1); @@ -96,11 +103,11 @@ const luaD_precall = function(L, off, nresults) { return true; } case CT.LUA_TLCL: { + let base; let p = func.value.p; let n = L.top - off - 1; let fsize = p.maxstacksize; - let base; - + luaD_checkstack(L, fsize); if (p.is_vararg) { base = adjust_varargs(L, p, n); } else { @@ -619,6 +626,7 @@ module.exports.SParser = SParser; module.exports.adjust_varargs = adjust_varargs; module.exports.luaD_call = luaD_call; module.exports.luaD_callnoyield = luaD_callnoyield; +module.exports.luaD_checkstack = luaD_checkstack; module.exports.luaD_growstack = luaD_growstack; module.exports.luaD_hook = luaD_hook; module.exports.luaD_pcall = luaD_pcall; diff --git a/src/lobject.js b/src/lobject.js index 79496d5..41c3a84 100644 --- a/src/lobject.js +++ b/src/lobject.js @@ -6,6 +6,7 @@ const assert = require('assert'); const defs = require('./defs.js'); const ljstype = require('./ljstype.js'); const ldebug = require('./ldebug.js'); +const ldo = require('./ldo.js'); const lstring = require('./lstring.js'); const luaconf = require('./luaconf.js'); const lvm = require('./lvm.js'); @@ -492,6 +493,7 @@ const luaO_pushvfstring = function(L, fmt, argp) { n += 2; i = e + 2; } + ldo.luaD_checkstack(L, 1); pushstr(L, fmt.slice(i)); if (n > 0) lvm.luaV_concat(L, n+1); return L.stack[L.top-1].svalue(); diff --git a/src/lvm.js b/src/lvm.js index 6e1012c..6711bfa 100644 --- a/src/lvm.js +++ b/src/lvm.js @@ -645,6 +645,7 @@ const luaV_execute = function(L) { if (b < 0) { b = n; /* get all var. arguments */ + ldo.luaD_checkstack(L, n); L.top = ra + n; } -- cgit v1.2.3-54-g00ecf From 6bd8fb685dd9a2c153ab040b1844cbcfecc699f0 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 22 May 2017 17:29:04 +1000 Subject: src/ldo.js: Add luaD_shrinkstack calls --- src/ldo.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src') diff --git a/src/ldo.js b/src/ldo.js index 5243f1f..10be56a 100644 --- a/src/ldo.js +++ b/src/ldo.js @@ -68,6 +68,24 @@ const luaD_checkstack = function(L, n) { luaD_growstack(L, n); }; +const stackinuse = function(L) { + let lim = L.top; + for (let ci = L.ci; ci !== null; ci = ci.previous) { + if (lim < ci.top) lim = ci.top; + } + assert(lim <= L.stack.length); + return lim + 1; /* part of stack in use */ +}; + +const luaD_shrinkstack = function(L) { + let inuse = stackinuse(L); + let goodsize = inuse + Math.floor(inuse / 8) + 2*lstate.EXTRA_STACK; + if (goodsize > luaconf.LUAI_MAXSTACK) + goodsize = luaconf.LUAI_MAXSTACK; /* respect stack limit */ + if (inuse <= (luaconf.LUAI_MAXSTACK - lstate.EXTRA_STACK) && goodsize < L.stack.length) + luaD_reallocstack(L, goodsize); +}; + /* ** Prepares a function call: checks the stack, creates a new CallInfo ** entry, fills in the relevant information, calls hook if needed. @@ -420,6 +438,7 @@ const recover = function(L, status) { L.ci = ci; L.allowhook = ci.callstatus & lstate.CIST_OAH; /* restore original 'allowhook' */ L.nny = 0; /* should be zero to be yieldable */ + luaD_shrinkstack(L); L.errfunc = ci.c_old_errfunc; return 1; /* continue running the coroutine */ }; @@ -563,6 +582,7 @@ const luaD_pcall = function(L, func, u, old_top, ef) { L.ci = old_ci; L.allowhook = old_allowhooks; L.nny = old_nny; + luaD_shrinkstack(L); } L.errfunc = old_errfunc; -- cgit v1.2.3-54-g00ecf From 6646bebd474b95a2d4cbb8558c0d1cb5b5353de0 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 22 May 2017 17:29:38 +1000 Subject: src/lstate.js: Remove useless luaE_freeCI We don't keep around a pool of callinfo objects --- src/lstate.js | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src') diff --git a/src/lstate.js b/src/lstate.js index d1789e8..5481f95 100644 --- a/src/lstate.js +++ b/src/lstate.js @@ -85,11 +85,6 @@ const luaE_extendCI = function(L) { return ci; }; -const luaE_freeCI = function(L) { - let ci = L.ci; - ci.next = null; -}; - const stack_init = function(L1, L) { L1.stack = new Array(BASIC_STACK_SIZE); // TODO: for now we don't care about the stack size L1.top = 0; @@ -105,7 +100,6 @@ const stack_init = function(L1, L) { const freestack = function(L) { L.ci = L.base_ci; - luaE_freeCI(L); L.stack = null; }; @@ -207,5 +201,4 @@ module.exports.lua_close = lua_close; module.exports.lua_newstate = lua_newstate; module.exports.lua_newthread = lua_newthread; module.exports.luaE_extendCI = luaE_extendCI; -module.exports.luaE_freeCI = luaE_freeCI; module.exports.luaE_freethread = luaE_freethread; -- cgit v1.2.3-54-g00ecf From 91e09ea32148c34965809b8d69987d439d389870 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 22 May 2017 18:06:40 +1000 Subject: Compare allowed stack indices to stack_last Not L.stack.length which is more equivalent to C's L->stacksize --- src/lapi.js | 3 ++- src/ldo.js | 8 +++++--- src/lstate.js | 7 +++++-- 3 files changed, 12 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/lapi.js b/src/lapi.js index 79c666c..ca5a8a0 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -94,7 +94,7 @@ const lua_checkstack = function(L, n) { let res; let ci = L.ci; assert(n >= 0, "negative 'n'"); - if (L.stack.length - L.top > n) /* stack large enough? */ + if (L.stack_last - L.top > n) /* stack large enough? */ res = true; else { /* no; need to grow stack */ let inuse = L.top + lstate.EXTRA_STACK; @@ -152,6 +152,7 @@ const lua_pushvalue = function(L, idx) { const lua_settop = function(L, idx) { let func = L.ci.funcOff; if (idx >= 0) { + assert(idx <= L.stack_last - (func + 1), "new top too large"); while (L.top < func + 1 + idx) L.stack[L.top++] = new TValue(CT.LUA_TNIL, null); L.top = func + 1 + idx; diff --git a/src/ldo.js b/src/ldo.js index 10be56a..7154a7f 100644 --- a/src/ldo.js +++ b/src/ldo.js @@ -43,6 +43,7 @@ const ERRORSTACKSIZE = luaconf.LUAI_MAXSTACK + 200; const luaD_reallocstack = function(L, newsize) { L.stack.length = newsize; + L.stack_last = newsize - lstate.EXTRA_STACK; }; const luaD_growstack = function(L, n) { @@ -64,7 +65,7 @@ const luaD_growstack = function(L, n) { }; const luaD_checkstack = function(L, n) { - if (L.stack.length - L.top <= n) + if (L.stack_last - L.top <= n) luaD_growstack(L, n); }; @@ -73,7 +74,7 @@ const stackinuse = function(L) { for (let ci = L.ci; ci !== null; ci = ci.previous) { if (lim < ci.top) lim = ci.top; } - assert(lim <= L.stack.length); + assert(lim <= L.stack_last); return lim + 1; /* part of stack in use */ }; @@ -107,7 +108,7 @@ const luaD_precall = function(L, off, nresults) { ci.nresults = nresults; ci.func = func; ci.top = L.top + defs.LUA_MINSTACK; - assert(ci.top <= L.stack.length); + assert(ci.top <= L.stack_last); ci.callstatus = 0; if (L.hookmask & defs.LUA_MASKCALL) luaD_hook(L, defs.LUA_HOOKCALL, -1); @@ -219,6 +220,7 @@ const luaD_hook = function(L, event, line) { ar.currentline = line; ar.i_ci = ci; ci.top = L.top + defs.LUA_MINSTACK; + assert(ci.top <= L.stack_last); L.allowhook = 0; /* cannot call hooks inside a hook */ ci.callstatus |= lstate.CIST_HOOKED; hook(L, ar); diff --git a/src/lstate.js b/src/lstate.js index 5481f95..b71e011 100644 --- a/src/lstate.js +++ b/src/lstate.js @@ -49,7 +49,8 @@ class lua_State { this.base_ci = new CallInfo(); // Will be populated later this.top = 0; this.ci = null; - this.stack = []; + this.stack = null; + this.stack_last = NaN; this.openupval = null; this.status = TS.LUA_OK; this.next = null; @@ -86,8 +87,10 @@ const luaE_extendCI = function(L) { }; const stack_init = function(L1, L) { - L1.stack = new Array(BASIC_STACK_SIZE); // TODO: for now we don't care about the stack size + L1.stack = new Array(BASIC_STACK_SIZE); L1.top = 0; + L1.stack_last = BASIC_STACK_SIZE - EXTRA_STACK; + /* initialize first ci */ let ci = L1.base_ci; ci.next = ci.previous = null; ci.callstatus = 0; -- cgit v1.2.3-54-g00ecf From a021614ebb948f218156cb38c4f441af904aa8c1 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 22 May 2017 18:36:25 +1000 Subject: src/lcode.js: Fix patchtestreg to actually modify the instruction --- src/lcode.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/lcode.js b/src/lcode.js index 6515d93..ea010aa 100644 --- a/src/lcode.js +++ b/src/lcode.js @@ -211,11 +211,14 @@ const luaK_getlabel = function(fs) { ** jump (that is, its condition), or the jump itself if it is ** unconditional. */ -const getjumpcontrol = function(fs, pc) { +const getjumpcontroloffset = function(fs, pc) { if (pc >= 1 && lopcodes.testTMode(fs.f.code[pc - 1].opcode)) - return fs.f.code[pc - 1]; + return pc - 1; else - return fs.f.code[pc]; + return pc; +}; +const getjumpcontrol = function(fs, pc) { + return fs.f.code[getjumpcontroloffset(fs, pc)]; }; /* @@ -226,7 +229,8 @@ const getjumpcontrol = function(fs, pc) { ** no register value) */ const patchtestreg = function(fs, node, reg) { - let i = getjumpcontrol(fs, node); + let pc = getjumpcontroloffset(fs, node); + let i = fs.f.code[pc]; if (i.opcode !== OpCodesI.OP_TESTSET) return false; /* cannot patch other instructions */ if (reg !== lopcodes.NO_REG && reg !== i.B) @@ -234,7 +238,7 @@ const patchtestreg = function(fs, node, reg) { else { /* no register to put value or register already has the value; change instruction to simple test */ - i = lopcodes.CREATE_ABC(OpCodesI.OP_TEST, i.B, 0, i.C); + fs.f.code[pc] = lopcodes.CREATE_ABC(OpCodesI.OP_TEST, i.B, 0, i.C); } return true; }; -- cgit v1.2.3-54-g00ecf From 5b764695bdc939784fd448fe6ba16ed3a9f44b19 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 22 May 2017 18:47:19 +1000 Subject: src/ldo.js: Add assert to luaD_reallocstack from C sources --- src/ldo.js | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/ldo.js b/src/ldo.js index 7154a7f..83f26cf 100644 --- a/src/ldo.js +++ b/src/ldo.js @@ -42,6 +42,8 @@ const seterrorobj = function(L, errcode, oldtop) { const ERRORSTACKSIZE = luaconf.LUAI_MAXSTACK + 200; const luaD_reallocstack = function(L, newsize) { + assert(newsize <= luaconf.LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); + assert(L.stack_last == L.stack.length - lstate.EXTRA_STACK); L.stack.length = newsize; L.stack_last = newsize - lstate.EXTRA_STACK; }; -- cgit v1.2.3-54-g00ecf