Repository navigation
Fence dedicated update-validation polls against replaced worker registrations #732
Description
Current correction
General's PR #738 is now 83a9b55, tree 0e5a9ca3b65a0acca3f51a774b19fbfbd156a669, integrated with current main, normal published Core 2.13.1 and the completed CI repair. It captures the admitted registration fence and hashed incarnation, then checks both under the worker-row lock in every claim/retry transaction. The dedicated poll returns the existing typed stale result; obsolete requests do not consume a task.
The four fencing source/test files are byte-identical to reviewed a7c250b. The earlier source cohort passed SQLite29/321 and PostgreSQL17.11/MySQL8.0.46 each29/305; the final affected controls passed5/68 and5/66 respectively. The detailed source receipt includes the retained unfixed counterfactual: three replacement cases fail while the unchanged control passes. Current integrated PHP8.3.36/PHPUnit11.5.55 controls pass38tests532assertions and Pint; all2176installed Corefiles match normal published2.13.1. Integrated PostgreSQL17.11/MySQL8.0.46 each pass29/305 with no failures/errors/skips, retaining11 discovery deprecations in unchanged test classes. General completed the exact integration review; the new-Core/current-main unfixed counterfactual again fails the three replacement cases while its unchanged control passes. Integrated receipt. Exact head completed19successful checks/7intended skips, and PR738 merged normally at4290969ee0f99751569ac853e4e98f244696c104 (23:36:35UTC) with the exact accepted tree; remote head is verified absent. All six independent main workflows passed, the last at23:49:57UTC. The integration review is General's; no new Security verdict is claimed.
Next: publish and verify the installed fixed PHP image. The issue remains open through published acceptance. Namespace, workflow contract/fingerprint, build, attempt, lease, lock-pressure and original timing thresholds remain; native work is separately owned.
Problem
At the inspected main baseline bce669d937594aebb6b2ee45992eeb40ba63e55e, the dedicated POST /api/worker/update-validation-tasks/poll path does not fence a worker registration that changes while the request is waiting.
WorkerController::pollUpdateValidationTasks() resolves one WorkerRegistration, and WorkflowUpdateValidationTaskBroker::poll() captures that model across every repeated LongPoller::until() probe. claim() eventually calls workerMatchesTask(), but that checks WorkerPollFence::isFresh($worker) against the captured model; it does not compare the snapshot with the current registration row. A replacement registration with the same worker ID can therefore supersede the request while the old request still appears fresh in memory.
If an update-validation task wakes the old request during that window, the old request can lease it using the superseded process's routing/capability snapshot. The active replacement does not receive the task until the lease expires, so synchronous update admission can time out or be handled by the wrong worker incarnation. This is a within-namespace availability/correctness defect; no cross-tenant access or data disclosure is currently evidenced.
Workflow, activity, and query polling already carry a WorkerPollFence snapshot and compare it with current durable registration state. The dedicated update-validation poll should preserve the same replacement boundary. Closed #35 covered task-kind/version fencing for multiplexed polls, not worker-registration replacement on this dedicated path.
Acceptance
- Capture the authoritative worker-registration fence/incarnation when the dedicated validation poll is admitted.
- Before every claim, including wake and lock-pressure retry attempts, compare that fence with the current registration inside the claim transaction; do not lease when it is superseded.
- Return the existing typed
stale_worker_registrationpoll result (or an equally explicit compatible refusal) so the obsolete request terminates without consuming the task. - Add a counterfactual that starts a waiting dedicated poll, replaces the same worker ID with the same queue/capabilities before the next probe, makes a validation task ready, and proves the old request does not lease it while the replacement can.
- Cover changed routing/capabilities and the unchanged-current-registration control. Preserve namespace, workflow contract/fingerprint, build-ID, attempt, and lock-pressure fences.
- Qualify SQLite, PostgreSQL, and MySQL behavior and the published PHP consumer path. Do not weaken long-poll, lease, or update-validation timeout thresholds.
Original inspection evidence
WorkflowUpdateValidationTaskBroker::poll()closes over the initially resolved model for the wholeLongPollerloop.workerMatchesTask()calls onlyWorkerPollFence::isFresh()on that captured model.- At initial inspection, no existing regression covered replacement during the dedicated validation poll; PR Fence dedicated update-validation polls to worker registrations #738 now provides it.
- Current-main source was re-fetched and inspected at the exact SHA above; no product source was changed during this review.
Activity
rmcdaniel commentedon Oct 24, 2026
Cross-implementation scope correction — 2026-10-24
The Rust Server owner corrected an earlier implementation assumption after inspecting native. Their current finding is:
worker_idduring each validation claim, but does not bind the waiting request to a registration incarnation;stale_worker_registrationresult on replacement, while native query polling does.This is owner-reported scope, not a completed Security source review. Ownership stays explicit: General owns this issue's PHP correction; the Rust Server owner will add a published-2.11.1 replacement counterfactual and native implementation under PR #712, then follow the accepted PHP validation behavior once released. Security will review native only after a formal terminal exact-SHA handoff. No shared/frozen fixture or source overlap is authorized by this note.
Do not close the PHP defect merely because native work exists, and do not claim native parity from the PHP correction. Each implementation needs its own exact evidence and installed/published acceptance.
rmcdaniel commentedon Oct 24, 2026
Merged normally as 4290969 at 2026-10-24 23:36:35 UTC, after the exact integrated head completed 19 successful checks and 7 intended skips. The merge tree is exactly the reviewed and qualified 0e5a9ca3b65a0acca3f51a774b19fbfbd156a669; its parents are current main
dcb2d718and reviewed head83a9b557. The merged remote branch is independently verified absent.Integrated source/review evidence includes current SQLite38/532, PostgreSQL17.11/MySQL8.0.46 each29/305, the current-Core unfixed counterfactual, and Sonar with zero unresolved issues/hotspots/new duplication. Existing discovery deprecations are retained explicitly.
All six independent main workflows on that exact merge are terminal successful:
The last workflow, CodeQL, completed at 23:49:57 UTC; total main activation-to-completion was 13 minutes 20 seconds. PHP completed at 23:44:52 UTC. Published PHP image acceptance remains outstanding; #732 stays open until that user-visible delivery is verified.