Fuel Network is an Ethereum L2 with a custom language, bridge and VM. The contest had a reward pool of $1M. Some big-time vulnerabilities were found in it, which are explained in the article.
In Fuel, messages can be sent from the L2 to the L1. The function update_execution_data adds all message ids to the MessageOut receipts of the current tx execution data even if the tx itself has reverted. Because the tx failed, it un-burns the tokens on the Fuel side. Even though the receipt has already been used with a revert, it can be used again on a successful call. This means it's possible to relay the same receipt multiple times (as long as it corresponds with another user transfer) to steal the user's funds.
The next two issues are compiler-related. Supertraits allow defining a function that is inherited by the contract implementing a given trait. Because these are sensitive functions, they should be locked down by the user-programmed behavior. In reality, all of the supertraits are available externally. The example they show is renounce_ownership being called by an arbitrary user, even though it shouldn't be callable.
The second compiler bug is an optimization issue. The compiler needs to decide which instructions are considered dead or in use. Alongside this, the DCE optimization pass will eliminate dead code that has no bearing on the output of the program. The Sway optimization step labeled instructions which actually had results in it as having dead code. This leads to incorrect values in a register and unexpected behavior by the program.
One of the standard libraries was missing overflow protections for smaller data types of the pow function such as u8, u16 and u32. If developers were expecting these overflows to be caught (which is a fair assumption), then incorrect math would occur, leading to potential security risks.
The Code Copy (CCP) instruction copies code from a specified contract into memory. When it does this, it charges gas based on the contract's total size rather than the number of bytes copied. If an offset into the contracts bytecode is given that exceeds the length then unwrap_or_default will return an empty slice. Later on, the write occurs to the contract but it contains a way to backfill it if the lengths don't line up with zeros. Neat!
Since the cost is associated with the size of the contract and nothing is copied, this ends up being super cheap! In blockchain-land, having expensive operations (like clearing memory) can lead to a denial of service (DoS) by resource exhaustion.
I took some time to look at the code when this contest happened but didn't find anything. It was interesting to see bugs in the compiler and bridge handling when I spent my time looking at the VM for memory safety issues. I enjoyed the write-up and learned a bunch more about proper target selection!