Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ses)!: Withdraw support for muli-lockdown #921

Merged
merged 6 commits into from
Nov 1, 2021
Merged

Conversation

kriskowal
Copy link
Member

@kriskowal kriskowal commented Oct 29, 2021

BREAKING CHANGE: Lockdown can no longer be called more than once. Lockdown no longer returns a boolean to indicate whether it was effective (true) or redundant (false). Instead, Lockdown will return undefined for its first invocation or throw an error otherwise.

Fixes #814

@kriskowal kriskowal force-pushed the 814-once-lockdown branch 3 times, most recently from 60e6264 to 139ac27 Compare October 29, 2021 23:24
Copy link
Contributor

@mhofman mhofman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine by me.

Reviewer nit: I usually try to review commit-by-commit, so when code is moved from one file to another, I prefer to have that as a separate commit if new code is added or left behind in the remaining file. For remain behind, I usually try to temporarily rename the source file if most of the content is extracted to another file so that the large moving content appears as a rename in git, and the small remaining content a new file.

Comment on lines 16 to 18
- *BREAKING CHANGE*: Lockdown may no longer be called more than once.
Lockdown no longer returns a boolean to indicate whether it was effective
(true) or redundant (false).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this mention an error is thrown if trying to lockdown twice? Seems weird to be explicit about former behavior, but not about new one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicated this. Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume we should update the PR description too?

Copy link
Contributor

@erights erights left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -53,7 +53,8 @@ import { makeCompartmentConstructor } from './compartment-shim.js';

const { details: d, quote: q } = assert;

let firstOptions;
/** @type {Error?} */
let priorLockdown = null;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why null rather than undefined?

Suggested change
let priorLockdown = null;
let priorLockdown;

I don't have any strong reason for preferring one over the other. But when there is no other reason, I generally default to undefined.

## Related

This error is distinct from `SES_MULTIPLE_INSTANCES`, which attempts to
distinguish the case where multiple Lockdown functions have been initialized.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The phrasing here is that the other error happens when "multiple Lockdown functions have been initialized", which might be a fine condition to detect and consider an error. However, the explanation in SES_MULTIPLE_INSTANCES.md below explains that error as indicating that multiple calls to lockdown occurred, where the calls were to different instances. From that text I would guess that multiple initializations, where only one is called, would not be an error.

Comment on lines 167 to 174
if (priorLockdown !== null) {
const error = new TypeError(
`Already lockded down (SES_ALREADY_LOCKED_DOWN)`,
);
// eslint-disable-next-line @endo/no-polymorphic-call
assert.note(error, assert.details`Prior call ${priorLockdown}`);
throw error;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested simplification. Use only the parts, if any, that you agree are an improvement

Suggested change
if (priorLockdown !== null) {
const error = new TypeError(
`Already lockded down (SES_ALREADY_LOCKED_DOWN)`,
);
// eslint-disable-next-line @endo/no-polymorphic-call
assert.note(error, assert.details`Prior call ${priorLockdown}`);
throw error;
}
assert(
priorLockdown === null,
X`Already locked down at ${priorLockdown} (SES_ALREADY_LOCKED_DOWN)`,
TypeError,
);

assuming an earlier

const { details: X } = assert;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, I really like this technique of capturing the earlier non-erroneous stack trace in an error in case of a later conflict.

Comment on lines 207 to 212
if (seemsToBeLockedDown()) {
// eslint-disable-next-line @endo/no-polymorphic-call
console.log('Seems to already be locked down. Skipping second lockdown');
return alreadyHardenedIntrinsics;
throw new TypeError(
`Already locked down but not by this SES instance (SES_MULTIPLE_INSTANCES)`,
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes clear which of the two conditions we treat as an error: where both lockdowns are called. This seems sensible.

Comment on lines 300 to 301
* We define hardenIntrinsics here so that options are in scope, but return
* it to the caller so we can benchmark repair separately from hardening.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other motivation is to prepare for vetted shims, which would run after repair and before hardening. In fact, this was the original and primary motivation.

domainTaming,
__allowUnsafeMonkeyPatching__,
};
priorLockdown = new TypeError('Prior lockdown (SES_ALREADY_LOCKED_DOWN)');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this will keep the scope of this function and of its predecessors in the stack around since engines (well at least v8) lazily generate the stack string. While this probably happens early enough in the program execution, it might be worth "touching" the priorLockdown.stack for good measure?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really? I have no direct knowledge, but I would have kept only a stack of PC addresses.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently JSC automatically generates the stack string if the scope would otherwise have been collected: https://twitter.com/filpizlo/status/1365140028247007234

Maybe v8 could perform some optimization in the case of strict functions and only keep the function name, and location around for Error.prepareStackTrace. If you look at the Twitter thread, they apparently ran into some other perf issues.

@kriskowal kriskowal merged commit 99752b0 into master Nov 1, 2021
@kriskowal kriskowal deleted the 814-once-lockdown branch November 1, 2021 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Once-only lockdown
3 participants