#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.

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?