From 79cc13c586603e940c3d88ad480bb8510f271359 Mon Sep 17 00:00:00 2001 From: Mathieu Hofman Date: Mon, 12 Dec 2022 23:50:37 +0000 Subject: [PATCH] feat(swingset): handle vc syscalls during transcript replay --- .../src/kernel/vat-loader/manager-helper.js | 8 +- .../vat-loader/manager-subprocess-xsnap.js | 3 +- .../src/kernel/vat-loader/transcript.js | 74 +++++++++++++++++-- 3 files changed, 75 insertions(+), 10 deletions(-) diff --git a/packages/SwingSet/src/kernel/vat-loader/manager-helper.js b/packages/SwingSet/src/kernel/vat-loader/manager-helper.js index 51f79e24ed40..e12bbee44e21 100644 --- a/packages/SwingSet/src/kernel/vat-loader/manager-helper.js +++ b/packages/SwingSet/src/kernel/vat-loader/manager-helper.js @@ -103,7 +103,7 @@ import { makeTranscriptManager } from './transcript.js'; * @param {KernelSlog} kernelSlog * @param {(vso: VatSyscallObject) => VatSyscallResult} vatSyscallHandler * @param {boolean} workerCanBlock - * @param {(vatID: any, originalSyscall: any, newSyscall: any) => Error | undefined} [compareSyscalls] + * @param {(vatID: any, originalSyscall: any, newSyscall: any) => import('./transcript.js').CompareSyscallsResult} [compareSyscalls] * @param {boolean} [useTranscript] * @returns {ManagerKit} */ @@ -247,7 +247,9 @@ function makeManagerKit( // but if the puppy deviates one inch from previous twitches, explode kernelSlog.syscall(vatID, undefined, vso); const vres = transcriptManager.simulateSyscall(vso); - return vres; + if (vres) { + return vres; + } } const vres = vatSyscallHandler(vso); @@ -256,7 +258,7 @@ function makeManagerKit( if (successFlag === 'ok' && data && !workerCanBlock) { console.log(`warning: syscall returns data, but worker cannot get it`); } - if (transcriptManager) { + if (transcriptManager && !transcriptManager.inReplay()) { transcriptManager.addSyscall(vso, vres); } return vres; diff --git a/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-xsnap.js b/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-xsnap.js index 9ffd730e917b..6ef10e60ed0f 100644 --- a/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-xsnap.js +++ b/packages/SwingSet/src/kernel/vat-loader/manager-subprocess-xsnap.js @@ -2,6 +2,7 @@ import { assert, details as X, q } from '@agoric/assert'; import { ExitCode } from '@agoric/xsnap/api.js'; import { makeManagerKit } from './manager-helper.js'; +import { requireIdenticalExceptStableVCSyscalls } from './transcript.js'; import { insistVatSyscallObject, @@ -55,7 +56,7 @@ export function makeXsSubprocessFactory({ const { name: vatName, metered, - compareSyscalls, + compareSyscalls = requireIdenticalExceptStableVCSyscalls, useTranscript, sourcedConsole, } = managerOptions; diff --git a/packages/SwingSet/src/kernel/vat-loader/transcript.js b/packages/SwingSet/src/kernel/vat-loader/transcript.js index e90100fea3c4..66b83885e3c1 100644 --- a/packages/SwingSet/src/kernel/vat-loader/transcript.js +++ b/packages/SwingSet/src/kernel/vat-loader/transcript.js @@ -1,5 +1,16 @@ import djson from '../../lib/djson.js'; +const missingSyscall = Symbol('missing transcript syscall'); +const extraSyscall = Symbol('extra transcript syscall'); + +/** @typedef {typeof missingSyscall | typeof extraSyscall | Error | undefined} CompareSyscallsResult */ + +/** + * @param {any} vatID + * @param {object} originalSyscall + * @param {object} newSyscall + * @returns {CompareSyscallsResult} + */ export function requireIdentical(vatID, originalSyscall, newSyscall) { if (djson.stringify(originalSyscall) !== djson.stringify(newSyscall)) { console.error(`anachrophobia strikes vat ${vatID}`); @@ -10,6 +21,39 @@ export function requireIdentical(vatID, originalSyscall, newSyscall) { return undefined; } +const vcSyscallRE = /^vc\.\d+\.\|(?:schemata|label)$/; + +/** + * @param {any} vatID + * @param {object} originalSyscall + * @param {object} newSyscall + * @returns {CompareSyscallsResult} + */ +export function requireIdenticalExceptStableVCSyscalls( + vatID, + originalSyscall, + newSyscall, +) { + const error = requireIdentical(vatID, originalSyscall, newSyscall); + + if (error) { + if ( + originalSyscall[0] === 'vatstoreGet' && + vcSyscallRE.test(originalSyscall[1]) + ) { + console.warn(` mitigation: ignoring extra vc syscall`); + return extraSyscall; + } + + if (newSyscall[0] === 'vatstoreGet' && vcSyscallRE.test(newSyscall[1])) { + console.warn(` mitigation: falling through to syscall handler`); + return missingSyscall; + } + } + + return error; +} + export function makeTranscriptManager( vatKeeper, vatID, @@ -59,13 +103,31 @@ export function makeTranscriptManager( let replayError; function simulateSyscall(newSyscall) { - const s = playbackSyscalls.shift(); - const newReplayError = compareSyscalls(vatID, s.d, newSyscall); - if (newReplayError) { - replayError = newReplayError; - throw replayError; + while (playbackSyscalls.length) { + const compareError = compareSyscalls( + vatID, + playbackSyscalls[0].d, + newSyscall, + ); + + if (compareError === missingSyscall) { + return undefined; + } + + const s = playbackSyscalls.shift(); + + if (!compareError) { + return s.response; + } else if (compareError !== extraSyscall) { + replayError = compareError; + break; + } + } + + if (!replayError) { + replayError = new Error(`historical inaccuracy in replay of ${vatID}`); } - return s.response; + throw replayError; } function finishReplayDelivery(dnum) {