Skip to content

PortAllocator.Release takes an integer, so it can release a port you no longer own #708

Description

@rainmanjam

From the poka-yoke audit of 2026-09-04. Lens: motion-step. Today: rung 0. Device reaches: rung 1 (Control). Silent. MEASURED.

The mistake available

Release a port you no longer own. internal/relay/ports.go:57 is
delete(a.held, p) — an integer in, no notion of who held it.

internal/engine/destinations.go:1336 stopBackup deliberately does not
write to d (documented at length at :1324-1330), so after it runs
d.backupPort and d.backupSub still name released resources.

Reachable path: reconcileBackup:1232 calls stopBackup(prev)
publishDest:1260 refuses (either e.stopped, or e.dests[id] != prev) →
prev stays in the map → StopWithin/teardownDest:1082 calls
stopBackup(prev) a second time.

Measured

HAZARD: after stopBackup the destination still records backupPort=65532
        and backupSub="dest:1:backup"
HAZARD: port 63981 is now held by two owners at once; Release() twice
        silently un-held B's port

What happens

Two engines are handed the same UDP port, and one programme's destination
receives another programme's stream — the exact failure PortAllocator's own
doc comment says the bind probe exists to prevent.

portFree() masks it only while the first owner's child is actually bound.
During supervisor restart backoff, or in the window between Allocate and
proc.Start(), it is wide open. Silent.

Today

None. The invariant lives in a doc comment.

Device → rung 1, Control

Allocate() (*Lease, error); Lease.Release() guarded by sync.Once; delete
the Release(int) method so a stale integer has no spelling.

Shares one device with the StopWithin leak issue — the same thirteen call
sites change shape for both, and doing them together is the only proposal in
this audit that reaches Control on two findings at once.

Interim → Warning: Release logs at error when !a.held[p].

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions