cloudflare / cloudflare/capnweb

`exportStub()` always allocates a new export ID, even for duplicate capabilities

Open
#141 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
4k
Forks
143
Avg merge
4d 6h
Merged PRs (30d)
7

Description

`exportStub()` deduplication by hook identity never fires.

https://github.com/cloudflare/capnweb/blob/c2bb17b940b23eb8ab89be1e85538493cb4552ad/src/rpc.ts#L369-L383

`reverseExports` is a `Map`, so dedup is by object identity.

https://github.com/cloudflare/capnweb/blob/c2bb17b940b23eb8ab89be1e85538493cb4552ad/src/serialize.ts#L275-L281

`devaluateHook` routes to either `exportStub` or `exportPromise` depending on whether the type is `"export"`/`"writable"` or `"promise"`. The promise path is intentionally never deduplicated (`exportPromise` comment: "Promises always use a new ID because otherwise the recipient could miss the resolution."), so the issue only concerns the paths that reach `exportStub`:

**`"stub"/"rpc-promise"`** https://github.com/cloudflare/capnweb/blob/c2bb17b940b23eb8ab89be1e85538493cb4552ad/src/serialize.ts#L218-L224 `hook` is set to the result of `hook.dup()` if `pathIfPromise` is falsy (the only time exportStub will be called).

**`"function"/"rpc-target"`** https://github.com/cloudflare/capnweb/blob/c2bb17b940b23eb8ab89be1e85538493cb4552ad/src/serialize.ts#L233-L234
`getHookForRpcTarget()` returns `hook.dup()` for "return" payloads (after correctly deduplicating in `rpcTargets` by target object identity), or a fresh `TargetStubHook.create()` each time for "params" payloads. Either way, a fresh object reaches `exportStub`.

**`"writable"`** https://github.com/cloudflare/capnweb/blob/c2bb17b940b23eb8ab89be1e85538493cb4552ad/src/serialize.ts#L251-L252
`getHookForWritableStream()` uses identical logic to `getHookForRpcTarget()` for our purposes, it deduplicates in `rpcTargets` for "return" payloads but returns `hook.dup()` each time, fresh `WritableStreamStubHook` each time for "params".

In all three cases, `dup()` always returns a new object for every hook type that can be exported (`RpcImportHook`, `TargetStubHook`, `WritableStreamStubHook`, `ReadableStreamStubHook`, `PayloadStubHook`). The two types where `dup()` returns `this` (`ErrorStubHook` and `MapVariableHook`) are never exported.

Because the stub fed to `exportStub` is always a newly created object, it will never be found in `reverseExports`, so we will always create a new export.

This means the same capability appearing twice always gets separate export IDs. This is what prevents the `remoteRefcount` bug (#140) from being triggered currently

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.