Found in a second reading of #1022 (sirtimid/gc-delivery-hardening).
What
The new guard rejects only when getOwner returns a different live endpoint. An object whose owner mapping is already gone returns undefined, falls through the guard, and reaches forgetKref, which tears down whatever c-list entry the calling endpoint has — including an import entry.
Failure scenario
- v1 abandons ko1. This PR now orphans it, so
getOwner(ko1) is undefined, while v2 still reaches it.
- v2 issues
syscall.abandonExports([ko1]) naming its import vref.
owner is undefined → the new guard does not throw.
checkReachable is false for the abandon path, so the reachability assertion performRetireImports would apply is skipped.
- v2's reachable import entry is torn down.
Counts stay self-consistent, because deleteCListEntry derives direction from the eref — so the audit sees nothing. The syscall has silently acted as an unchecked retireImports on an object v2 still reaches.
Introduced by this PR (the guard and the orphaning are both new).
Suggested fix
Also require that the calling endpoint's entry is an export:
parseRef(krefToEref(endpointId, kref)).direction === 'export'
That is the property abandonExports actually means, and it rejects the import case whether or not an owner is recorded.
Found in a second reading of #1022 (
sirtimid/gc-delivery-hardening).What
The new guard rejects only when
getOwnerreturns a different live endpoint. An object whose owner mapping is already gone returnsundefined, falls through the guard, and reachesforgetKref, which tears down whatever c-list entry the calling endpoint has — including an import entry.Failure scenario
getOwner(ko1)isundefined, while v2 still reaches it.syscall.abandonExports([ko1])naming its import vref.ownerisundefined→ the new guard does not throw.checkReachableis false for the abandon path, so the reachability assertionperformRetireImportswould apply is skipped.Counts stay self-consistent, because
deleteCListEntryderives direction from the eref — so the audit sees nothing. The syscall has silently acted as an uncheckedretireImportson an object v2 still reaches.Introduced by this PR (the guard and the orphaning are both new).
Suggested fix
Also require that the calling endpoint's entry is an export:
That is the property
abandonExportsactually means, and it rejects the import case whether or not an owner is recorded.