Assessment reports>Chainflip Backend>High findings>Witnesser rotation may queue call multiple times
Category: Coding Mistakes

Witnesser rotation may queue call multiple times

High Severity
Low Impact
Low Likelihood

Description

The witnesser pallet is responsible of keeping track of how many witnessers have observed an event as well as dispatching a call to act on the event once enough witnessers have witnessed it. This functionality is implemented by the witness_at_epoch function.

The pallet can be paused in case of emergencies via the SafeMode pallet. If the pallet is paused, witness_at_epoch does not dispatch the calls immediately. Instead, the calls are scheduled for execution by appending them to a queue (WitnessedCallsScheduledForDispatch). The calls will be dispatched by on_idle once the safe mode is disabled.

Normally, the same call cannot be executed twice; the pallet keeps track of which calls have executed in each previous valid epoch, and witness_at_epoch does not dispatch the call if it has already executed. However, on_idle does not perform the same check, so in principle the same call could be dispatched multiple times if it was added to the WitnessedCallsScheduledForDispatch queue more than once.

Scenario 1

In a scenario where safe mode is enabled and a witnesser rotation is imminent, the same event could be observed by two different majority sets of validators in two different epochs and therefore be pushed to the queue twice.

Scenario 2

When a call is scheduled, it executes any time there is sufficient weight at the end of the block (on_idle).

There is a force_witness function that allows the root origin to bypass replay and multi-sig checks and execute a call immediately. This action uses the dispatch_call function, which adds the call hash to the CallHashExecuted pallet storage; but if a call is already scheduled, it is no longer subject to replay protections.

Impact

In an unlikely edge case scenario, the same call could be scheduled and eventually dispatched twice, duplicating the effects of an event.

Additionally, if a call were scheduled and the force_witness function forced the call to be made, it would be replayed. Though the force_witness function intends to allow calls to be replayed, the scheduled calls are not intended to be replayed. So, we believe this behavior is unintended.

Recommendations

Ensure the event was not already executed in on_idle before dispatching it, similarly to how witness_at_epoch performs the check.

Remediation

This issue has been acknowledged by Chainflip Labs, and a fix was implemented in commit 20641113.

The patch ensures that the on_idle function also checks that the call has not been dispatched yet by checking CallHashExecuted.

Zellic © 2024Back to top ↑