#43190 [BC-Critical] Deadlock in `submit_transaction()`
Submitted on Apr 3rd 2025 at 14:45:52 UTC by @br0nz3p1ck4x3 for Attackathon | Movement Labs
Report ID: #43190
Report Type: Blockchain/DLT
Report severity: Critical
Target: https://github.com/immunefi-team/attackathon-movement/tree/main/protocol-units/execution/maptos/opt-executor
Impacts:
Temporary freezing of network transactions by delaying one block by 500% or more of the average block time of the preceding 24 hours beyond standard difficulty adjustments
Description
Description
Inside submit_transactions(), the variable transactions_in_flight will be written and read to. In the following code-block, a read() is done:
let in_flight = {
let transactions_in_flight = self.transactions_in_flight.read().unwrap();
transactions_in_flight.get_count()
};A few lines below the read(), we can find write()
{
let mut transactions_in_flight = self.transactions_in_flight.write().unwrap();
transactions_in_flight.increment(now, 1);
}The problem here is the dangerous usage of the read() and the write() methods. In Rust, a lock is placed during the read() and the write() operation. If the current thread attempts an access on a lock held by a current thread, this will cause a panic. We can see the following from the standard library
https://github.com/rust-lang/rust/blob/e0883a2a6c8e4afcb6e26a182885f687ba63a7f5/library/std/src/sync/poison/rwlock.rs#L441
/// # Panics
///
/// This function might panic when called if the lock is already held by the current thread.As such, the submit_transaction() function is susceptible to a deadlock when it's under heavy load, which will lead to a panic and break the sequencer.
Impact
The deadlock will stall the sequencer, degrading the network performance and/or stalling the chain, depending on which sequencer is stalled.
Recommended Mitigation Steps
Use common patterns that eliminate the chance of deadlock when using read/write locks.
Proof of Concept
Proof of Concept
This proof of concept is straight-forward. This will happen under heavy workload because this will increase the chances of a thread trying to write/read at the same time.
Step one: Network is digested, sequencer_x is under heavy workload
Step two: thread_12 tries to read() the variable transactions_in_flight() while it still has a lock from a previous write() call.
Step three: A panic will happen, leading to a deadlock and stalling sequencer_x
Was this helpful?