diff --git a/packages/xsnap/src/xsnap.c b/packages/xsnap/src/xsnap.c index 27cc843ff0f9..cbc501cd8dcf 100644 --- a/packages/xsnap/src/xsnap.c +++ b/packages/xsnap/src/xsnap.c @@ -204,19 +204,7 @@ int main(int argc, char* argv[]) int error = 0; int interval = 0; int freeze = 0; - xsCreation _creation = { - 16 * 1024 * 1024, /* initialChunkSize */ - 16 * 1024 * 1024, /* incrementalChunkSize */ - 1 * 1024 * 1024, /* initialHeapCount */ - 1 * 1024 * 1024, /* incrementalHeapCount */ - 4096, /* stackCount */ - 32000, /* keyCount */ - 1993, /* nameModulo */ - 127, /* symbolModulo */ - 8192 * 1024, /* parserBufferSize */ - 1993, /* parserTableModulo */ - }; - xsCreation* creation = &_creation; + int parserBufferSize = 8192 * 1024; txSnapshot snapshot = { SNAPSHOT_SIGNATURE, @@ -279,6 +267,15 @@ int main(int argc, char* argv[]) return 1; } } + else if (!strcmp(argv[argi], "-s")) { + argi++; + if (argi < argc) + parserBufferSize = 1024 * atoi(argv[argi]); + else { + fxPrintUsage(); + return 1; + } + } else if (!strcmp(argv[argi], "-v")) { printf("xsnap %s (XS %d.%d.%d)\n", XSNAP_VERSION, XS_MAJOR_VERSION, XS_MINOR_VERSION, XS_PATCH_VERSION); return 0; @@ -287,6 +284,21 @@ int main(int argc, char* argv[]) return 1; } } + fprintf(stderr, "@@parserBufferSize %d", parserBufferSize); + xsCreation _creation = { + 16 * 1024 * 1024, /* initialChunkSize */ + 16 * 1024 * 1024, /* incrementalChunkSize */ + 1 * 1024 * 1024, /* initialHeapCount */ + 1 * 1024 * 1024, /* incrementalHeapCount */ + 4096, /* stackCount */ + 32000, /* keyCount */ + 1993, /* nameModulo */ + 127, /* symbolModulo */ + parserBufferSize, /* parserBufferSize */ + 1993, /* parserTableModulo */ + }; + xsCreation* creation = &_creation; + if (gxCrankMeteringLimit) { if (interval == 0) interval = 1; @@ -850,11 +862,12 @@ void fxPatchBuiltIns(txMachine* machine) void fxPrintUsage() { - printf("xsnap [-h] [-f] [-i ] [-l ] [-m] [-r ] [-s] [-v]\n"); + printf("xsnap [-h] [-f] [-i ] [-l ] [-s ] [-m] [-r ] [-s] [-v]\n"); printf("\t-f: freeze the XS machine\n"); printf("\t-h: print this help message\n"); printf("\t-i : metering interval (default to 1)\n"); printf("\t-l : metering limit (default to none)\n"); + printf("\t-s : parser buffer size, in kB (default to 8192)\n"); printf("\t-r : read snapshot to create the XS machine\n"); printf("\t-v: print XS version\n"); } diff --git a/packages/xsnap/src/xsnap.js b/packages/xsnap/src/xsnap.js index b82b62226c72..5cf0c48683f3 100644 --- a/packages/xsnap/src/xsnap.js +++ b/packages/xsnap/src/xsnap.js @@ -46,6 +46,7 @@ function echoCommand(arg) { * @param {(request:Uint8Array) => Promise} [options.handleCommand] * @param {string=} [options.name] * @param {boolean=} [options.debug] + * @param {number=} [options.parserBufferSize] in kB (must be an integer) * @param {string=} [options.snapshot] * @param {'ignore' | 'inherit'} [options.stdout] * @param {'ignore' | 'inherit'} [options.stderr] @@ -58,6 +59,7 @@ export function xsnap(options) { name = '', handleCommand = echoCommand, debug = false, + parserBufferSize = undefined, snapshot = undefined, stdout = 'ignore', stderr = 'ignore', @@ -82,10 +84,16 @@ export function xsnap(options) { /** @type {Deferred} */ const vatExit = defer(); - const args = snapshot ? ['-r', snapshot] : []; + const args = []; + if (snapshot) { + args.push('-r', snapshot); + } if (meteringLimit) { args.push('-l', `${meteringLimit}`); } + if (parserBufferSize) { + args.push('-s', `${parserBufferSize}`); + } const xsnapProcess = spawn(bin, args, { stdio: ['ignore', stdout, stderr, 'pipe', 'pipe'], diff --git a/packages/xsnap/test/test-xsnap.js b/packages/xsnap/test/test-xsnap.js index e3b3d163849c..49086b90dba4 100644 --- a/packages/xsnap/test/test-xsnap.js +++ b/packages/xsnap/test/test-xsnap.js @@ -435,50 +435,40 @@ test('property name space exhaustion: orderly fail-stop', async t => { } }); -test('parser buffer exhaustion: orderly fail-stop', async t => { - const grow = ` - const send = it => issueCommand(ArrayBuffer.fromString(it)); - let expr = '1+1'; +(() => { + const grow = qty => ` + const send = it => issueCommand(ArrayBuffer.fromString(JSON.stringify(it))); + let expr = \`"\${Array(${qty}).fill('abcd').join('')}"\`; try { - for(;;) { - send(expr.length); - eval(expr); - expr = expr + ',' + expr; - } + eval(expr); + send(expr.length); } catch (err) { - // buffer exhaustion should not be catchable! send(err.message); } `; for (const debug of [false, true]) { - const opts = options(); - const vat = xsnap({ ...opts, debug }); - t.teardown(() => vat.terminate()); - // eslint-disable-next-line no-await-in-loop - await t.throwsAsync(vat.evaluate(grow), { message: /exited with code 7$/ }); - t.deepEqual(opts.messages.slice(0, 19), [ - '3', - '7', - '15', - '31', - '63', - '127', - '255', - '511', - '1023', - '2047', - '4095', - '8191', - '16383', - '32767', - '65535', - '131071', - '262143', - '524287', - '1048575', - ]); + for (const [parserBufferSize, qty, failure] of [ + [undefined, 100, null], + [undefined, 8192 * 1024 + 100, 'buffer overflow'], + [2, 10, null], + [2, 50000, 'buffer overflow'], + ]) { + test(`parser buffer size ${parserBufferSize || + 'default'}k; rep ${qty}; debug ${debug}`, async t => { + const opts = options(); + const vat = xsnap({ ...opts, debug, parserBufferSize }); + t.teardown(() => vat.terminate()); + const expected = failure ? [failure] : [qty * 4 + 2]; + // eslint-disable-next-line no-await-in-loop + await t.notThrowsAsync(vat.evaluate(grow(qty))); + t.deepEqual( + expected, + opts.messages.map(txt => JSON.parse(txt)), + ); + }); + } } -}); +})(); (() => { const challenges = [