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

feat(non-trapping-shim): ponyfill and shim for the non-trapping integrity trait #2673

Open
wants to merge 4 commits into
base: markm-prepare-for-stabilize-2
Choose a base branch
from

Conversation

erights
Copy link
Contributor

@erights erights commented Dec 30, 2024

Staged on #2679

Closes: #XXXX
Refs: https://github.com/tc39/proposal-stabilize #1756 #1912 #2675

Description

A ponyfill and shim for the no-trapping integrity level of https://github.com/tc39/proposal-stabilize

Done by itself only implying frozen, since we cannot yet ponyfill or shim "fix" or "permit-overrides". However, no-trapping would actually need to imply all other integrity levels, since the methods for querying or enabling these integrity levels would no longer trap once an object is non-trapping.

The names isNoTrapping and suppressTrapping are of course placeholders for whatever real names we decide on. But since no-trapping must imply all others, the name chosen should be appropriate for this maximal explicit integrity level. Currently, https://github.com/tc39/proposal-stabilize suggests that should be isStable and stabilize. If we revise this PR in that direction, we should also rename the package to stabilize-shim. A reason not to yet, is our inability to shim "fix" and "permit-overrides", which would be implied.

Security Considerations

The point. The no-trapping integrity level enables practical programming patterns to protect themselves from reentrancy hazards, without introducing a check for whether something is a proxy. Thus, it does not damage practical membrane transparency.

Scaling Considerations

There is a hidden WeakMap and WeakSet involved. As with passStyleOf, use by agoric-sdk will need to arrange to build this package once with the real WeakMap and WeakSet, and to deeply endow bindings of Reflect, Object, and Proxy together. Otherwise, we recapitulate the scaling problems that passStyleOf had suffered from.

Documentation Considerations

The differences introduced by this PR should largely be covered by documentation for https://github.com/tc39/proposal-stabilize , which will normally only be of concern to the expert low-level programmers of our platforms -- both endo and agoric-sdk. For other programmers, this should only relieve us of the need to explain how to defend against reentrancy hazards that this PR makes go away.

Testing Considerations

Since this is a shim for a proposal, it should eventually be covered by test262 tests. The testing of the new integrity level should be at least as exhaustive as the test262 testing of other integrity levels.

Compatibility Considerations

To make this SES compatible, we need to extend permits.js to permit the four new methods. To use this to make SES safer, we will need to modify harden so that it makes all hardened objects also be non-trapping.

passStyleOf would need to check that copy-data (copyList, copyRecord, tagged) are no-trapping rather than just frozen.

At the agoric-sdk level, all virtual and durable object representatives are already hardened, so the previous changes will also make all these representatives non-trapping. Thus, we don't need any virtual or durable state to keep track of whether a virtual or durable object is non-trapping. Nor do we lose the bookkeeping when a new representative is created, even though this shim should be made by liveslots only with the real WeakMap and WeakSet, which is not virtual/durable-aware.

Separately,
The original Proxy is both constructible (i.e., responsive to new) and
lacks a prototype property. The closest we can come to this is to set ProxyPlus.prototype to undefined.
SES should then explicitly permit this extra prototype property with an undefined value, so we don't get unnecessary warning spam for a case we will now expect.

Upgrade Considerations

In theory, old code could use Proxy in ways that this PR would break. But this is unlikely in general, and highly unlikely for any code in or depending on endo. Thus, practically, none.

@erights erights self-assigned this Dec 30, 2024
@erights erights force-pushed the markm-no-trapping-shim branch 3 times, most recently from e807308 to 8f8d5cf Compare December 31, 2024 04:51
@erights erights changed the title feat(no-trapping-shim) feat(no-trapping-shim) ponyfill and shim for the no-trapping integrity level Dec 31, 2024
@erights erights marked this pull request as ready for review December 31, 2024 05:23
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.

I've only skimmed through quickly, but from what I can tell this is following closely (but not fully) the letter of the proposed spec: that no state is held on the proxy and checks are instead made on the shadow target. However I have a feeling that we can prove an equivalent approach would be to store state on proxy objects only, since by definition ordinary objects can't be trapping. This should reduce the cost of side tables necessary for the implementation.

packages/no-trapping-shim/src/no-trapping-pony.js Outdated Show resolved Hide resolved
packages/no-trapping-shim/src/no-trapping-pony.js Outdated Show resolved Hide resolved
packages/no-trapping-shim/src/no-trapping-pony.js Outdated Show resolved Hide resolved
packages/no-trapping-shim/src/no-trapping-pony.js Outdated Show resolved Hide resolved
@@ -0,0 +1,143 @@
# Change Log
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kriskowal This first commit is the pristine state produced from

$ ./scripts/create-package.sh no-trapping-shim

See the voluminous CHANGELOG.md in that first commit, which I truncated in a subsequent commit. Is this a tooling bug, where the thing that updated CHANGELOG.md in general does not know about the one that serves as a template for new packages?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kriskowal should I file a bug on this? Do you expect to fix it by changing the tooling for updating CHANGELOG.md or by changing create-package.sh?

@erights
Copy link
Contributor Author

erights commented Dec 31, 2024

@kriskowal , at https://github.com/endojs/endo/actions/runs/12563852500/job/35026252842?pr=2673 I am getting lint errors that seem clearly unrelated to this PR. Any idea what happened?

Update: See #2676 (comment) , which establishes that these lint errors are somehow dependent on this PR.

Update: The only thing I could think of was the devDepencies from this PR to ses-level stuff. So I removed those, and checked that the resulting yarn.lock entry for no-trapping-shim is like that for immutable-arraybuffer. However, I'm still getting those lint errors, and so am completely flummoxed. Help!

[info] Converting project at ./packages/lockdown
packages/marshal/src/encodeToCapData.js:190:9 - error TS2322: Type '{ [k: string]: string | number | boolean | EncodingClass<"NaN"> | EncodingClass<"undefined"> | EncodingClass<"Infinity"> | EncodingClass<"-Infinity"> | ... 9 more ... | null; }' is not assignable to type 'Encoding'.
  Type '{ [k: string]: string | number | boolean | EncodingClass<"NaN"> | EncodingClass<"undefined"> | EncodingClass<"Infinity"> | EncodingClass<"-Infinity"> | ... 9 more ... | null; }' is not assignable to type 'EncodingClass<"error"> & { name: string; message: string; errorId?: string | undefined; cause?: Encoding | undefined; errors?: Encoding[] | undefined; }'.
    Property ''@qclass'' is missing in type '{ [k: string]: string | number | boolean | EncodingClass<"NaN"> | EncodingClass<"undefined"> | EncodingClass<"Infinity"> | EncodingClass<"-Infinity"> | ... 9 more ... | null; }' but required in type 'EncodingClass<"error">'.

190         return fromEntries(
            ~~~~~~

  packages/marshal/src/types.js:25:16
    25  * @typedef {{ '@qclass': T }} EncodingClass
                      ~~~~~~~~~
    ''@qclass'' is declared here.

packages/pass-style/src/symbol.js:14:7 - error TS2578: Unused '@ts-expect-error' directive.

14       // @ts-expect-error It doesn't know name cannot be a symbol
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

packages/ses/src/error/assert.js:308:5 - error TS25[78](https://github.com/endojs/endo/actions/runs/12564819013/job/35028359869?pr=2673#step:8:79): Unused '@ts-expect-error' directive.

308     // @ts-expect-error TS still confused by symbols as property names
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

packages/ses/src/make-hardener.js:241:21 - error TS2578: Unused '@ts-expect-error' directive.

241                     // @ts-expect-error TS should know FERAL_STACK_GETTER
                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

@erights
Copy link
Contributor Author

erights commented Dec 31, 2024

@mhofman , all your suggestions addressed. PTAL. Thanks.

@erights erights force-pushed the markm-no-trapping-shim branch 2 times, most recently from c68e9be to c52cf30 Compare December 31, 2024 21:42
@erights erights force-pushed the markm-no-trapping-shim branch 2 times, most recently from cb1e1f6 to 5c38d8c Compare December 31, 2024 23:11
@erights erights force-pushed the markm-no-trapping-shim branch from 98e6396 to f7d527c Compare January 1, 2025 00:05
@mhofman mhofman self-requested a review January 1, 2025 03:01
@mhofman
Copy link
Contributor

mhofman commented Jan 1, 2025

I have a feeling that we can prove an equivalent approach would be to store state on proxy objects only, since by definition ordinary objects can't be trapping.

It was obviously too late for me to form a proper intuition there. The non-trapping trait is not proxy specific, but actually a regular object's trait about any proxy that may target them. Otherwise we would have a predicate that can ask "is this a proxy object behaving exotically". By defining the trait on objects in general, we maintain the transparency of proxies.

As such, I was mistaken, and I don't think it's possible to only store the trait exclusively on proxy object.

@erights erights requested review from michaelfig and removed request for mhofman and michaelfig January 1, 2025 03:55
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.

I've only reviewed the implementation so far. Looks good overall, except I don't understand this shouldThrow condition.

Also a potential optimization: when adding a proxy to the noTrappingSet, we could eagerly redefine all own properties of handlerPlus to undefined (including the originalHandler). To make that easier we just need to save handlerPlus in the proxyHandlerMap instead of the original handler. If we do this, the original handler would become unreferenced when the proxy is found to be non-trapping, giving the nice property that it can be collected (this would be true with a native spec implementation).

Comment on lines 40 to 43
if (shouldThrow) {
throw TypeError(
`'isNoTrapping' proxy trap does not reflect 'isNoTrapping' of proxy target (which is '${ofTarget}')`,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand this shouldThrow condition. A proxy breaking invariants always results in a thrown error afaik. For example the only difference between Object.isExtensible and Reflect.isExtensible is when the target is not an object.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe I missed something, but I still see this shouldThrow which I don't understand the purpose of. Why are we not throwing unconditionally here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

GOOD CATCH!

I am surprised I somehow closed this conversation. At that moment I must have thought I was looking at something else. I don't remember, but I never thought I had done this one.

THANKS!

packages/no-trapping-shim/src/no-trapping-pony.js Outdated Show resolved Hide resolved
packages/no-trapping-shim/src/no-trapping-pony.js Outdated Show resolved Hide resolved
packages/no-trapping-shim/src/no-trapping-pony.js Outdated Show resolved Hide resolved
@erights erights changed the title feat(no-trapping-shim) ponyfill and shim for the no-trapping integrity level feat(no-trapping-shim) ponyfill and shim for the no-trapping integrity trait Jan 2, 2025
@erights erights changed the title feat(no-trapping-shim) ponyfill and shim for the no-trapping integrity trait feat(no-trapping-shim): ponyfill and shim for the no-trapping integrity trait Jan 2, 2025
@erights erights force-pushed the markm-no-trapping-shim branch from f7d527c to 29223ef Compare January 2, 2025 21:41
@erights erights changed the base branch from master to markm-prepare-for-stabilize-2 January 2, 2025 21:41
@erights
Copy link
Contributor Author

erights commented Jan 2, 2025

Note to reviewers: In relayering this on #2679 but before addressing the unresolved conversations above, I did some squashing.

@erights erights force-pushed the markm-no-trapping-shim branch 2 times, most recently from 0c6aa83 to 7bcce04 Compare January 2, 2025 23:34
@erights erights changed the base branch from markm-prepare-for-stabilize-2 to master January 2, 2025 23:35
@erights erights changed the base branch from master to markm-prepare-for-stabilize-2 January 2, 2025 23:35
@erights erights force-pushed the markm-prepare-for-stabilize-2 branch from b12eb43 to 9925c0c Compare January 2, 2025 23:38
@erights erights changed the title feat(no-trapping-shim): ponyfill and shim for the no-trapping integrity trait feat(no-trapping-shim): ponyfill and shim for the non-trapping integrity trait Jan 2, 2025
@erights erights force-pushed the markm-prepare-for-stabilize-2 branch from 9925c0c to 6ff2c89 Compare January 2, 2025 23:43
@erights erights force-pushed the markm-no-trapping-shim branch from 7bcce04 to 29c9d47 Compare January 2, 2025 23:45
@erights erights changed the title feat(no-trapping-shim): ponyfill and shim for the non-trapping integrity trait feat(non-trapping-shim): ponyfill and shim for the non-trapping integrity trait Jan 2, 2025
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.

Reopened the shouldThrow conversation as I don't see anything changed

Comment on lines 40 to 43
if (shouldThrow) {
throw TypeError(
`'isNoTrapping' proxy trap does not reflect 'isNoTrapping' of proxy target (which is '${ofTarget}')`,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe I missed something, but I still see this shouldThrow which I don't understand the purpose of. Why are we not throwing unconditionally here?

packages/no-trapping-shim/src/no-trapping-pony.js Outdated Show resolved Hide resolved
@erights erights force-pushed the markm-prepare-for-stabilize-2 branch from 6ff2c89 to c3b3d92 Compare January 3, 2025 19:54
@mhofman
Copy link
Contributor

mhofman commented Jan 5, 2025

In theory, old code could use Proxy in ways that this PR would break. But this is unlikely in general, and highly unlikely for any code in or depending on endo. Thus, practically, none.

As discussed in #2675 (review), this turns out to not be so simple.

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.

2 participants