Skip to content

FLOW-6: Unfinalized Processing Requests Can Wedge the Global Queue #21

@liobrasil

Description

@liobrasil

Severity: Medium

Files Affected

  • cadence/contracts/FlowYieldVaultsEVM.cdc
  • cadence/contracts/FlowYieldVaultsTransactionHandler.cdc
  • solidity/src/FlowYieldVaultsRequests.sol

Description

The EVM-side queue pendingRequestIds in FlowYieldVaultsRequests represents "not finalized yet" work: requests are added in _createRequest() and removed only in completeProcessing() via _removePendingRequest().

When the Cadence worker runs, FlowYieldVaultsTransactionHandler.Handler.executeTransaction() always calls worker.processRequests(startIndex: 0, ...), and FlowYieldVaultsEVM.Worker.getPendingRequestsFromEVM() fetches entries via getPendingRequestsUnpacked() then processes each with processRequestSafely(), which enforces request.status == PENDING as a precondition.

However, startProcessing() in FlowYieldVaultsRequests transitions a request PENDING -> PROCESSING but does not remove it from pendingRequestIds / pendingRequestIdsByUser, so getPendingRequestsUnpacked() can legitimately return requests in PROCESSING.

If any request becomes PROCESSING and is not subsequently finalized by a successful completeProcessing() call, the worker will hit that item (often at the head because automation always starts from index 0), fail its PENDING precondition, and prevent forward progress.

Common Root Cause (shared with FLOW-2 / FLOW-3 / FLOW-6)

startProcessing() transitions PENDING → PROCESSING but does not remove the request from pendingRequestIds / pendingRequestIdsByUser (removal only happens in completeProcessing()). If completeProcessing() reverts (e.g., ERC20 refund approval/transferFrom, or out-of-gas in _removePendingRequest()), the Worker does not revert the Cadence transaction, so Cadence-side state may commit while the EVM request remains PROCESSING in the pending queues. Since cancelRequest()/dropRequests() only operate on PENDING, there is no in-protocol escape hatch.

Recommendation

  • Make the queue semantics explicit and consistent across modules
  • Add a deterministic recovery path for PROCESSING items (e.g., COA/owner-only "force finalize/fail after timeout")

Parent Issue: #15

Metadata

Metadata

Assignees

Labels

MediumMedium severity security finding⎈ QuantStampQuantStamp audit finding

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions