Attackathon _ Fuel Network 32825 - [Blockchain_DLT - High] Consensus between -bit and -bit system ca
Submitted on Thu Jul 04 2024 07:53:29 GMT-0400 (Atlantic Standard Time) by @ret2happy for Attackathon | Fuel Network
Report ID: #32825
Report type: Blockchain/DLT
Report severity: High
Target: https://github.com/FuelLabs/fuel-vm/tree/0e46d324da460f2db8bcef51920fb9246ac2143b
Impacts:
Unintended permanent chain split requiring hard fork (network partition requiring hard fork)
Description
Consensus between 32-bit and 64-bit system can fail
Brief/Intro
Consensus between 32bit and 64bit systems might fail due to a failure type conversion in the LDC opcode implementation [1]. Executing malicious LDC opcode leads to the different result (PanicReason) on validators with different architecture.
Vulnerability Details
In the interpreter/blockchain.rs#L597-L607 [1], the loaded contract length is the 64-bit register value. However, it uses padded_len_usize
to convert u64
to usize
. Note that usize
can either be 32 bit or 64 bit depending on the architecture.
If the validators running on the
32-bit
system (e.g., wasm), theusize
here isu32
, and this would causePanicReason::MemoryOverflow
as the following code shows.If the validators running on the
64-bit
system, theusize
here isu64
, andtry_into
conversion will success.
Consider we already set the $rC to u64::MAX (will demonstrate it more detail in PoC), we will get PanicReason::MemoryOverflow
on 32-bit system, and get PanicReason::ContractMaxSize
on 64-bit system.
Impact Details
Since executing the same program result in different panic reason on different validators, the consensus is broken. More specifically, because the receipt contains the panic reason and ultimately influences the block header hash, the block header hash will be incorrect. This would read to the network fork.
References
[1] https://github.com/FuelLabs/fuel-vm/blob/ae91bbdc804b870a84efe0bc6d18f43a79f351e7/fuel-vm/src/interpreter/blockchain.rs#L597-L607
Proof of concept
Proof of Concept
The following PoC get PanicReason::ContractMaxSize
on a 64bit system but get PanicReason::MemoryOverflow
on a 32bit system.
To test conveniently on the 32-bit system, I add the padded_len_usize_32bit
function to simulate the u32
type for 32-bit system, in the fuel-types/src/bytes.rs
:
And I also change the fuel-vm/src/interpreter/blockchain.rs#L597
from padded_len_usize
to padded_len_usize_32bit
:
Runnning the above PoC, we get different result on 32/64-bit system.
Moreover, the simplified fuel PoC program could be:
Last updated