xanoutas/solidity-security-findings huggingface dataset:
`MIPS` - Incorrect handling of memory mapping
Audit: 2024-07-optimism-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/MIPS.sol#L176-L179


# Vulnerability details

## Impact
Incorrect resolution of dispute game - True root claim is resolved as False

## Proof of Concept
```c
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
```
The above code-snippet shows an interface of low-level `mmap` function.

The first parameter is a preferred addr for mapping the memory, but when it is set to NULL(which is most of the case), the address will be determine in run-time, based on different factors like:

- Gaps in memory, for example, if memories that had been allocated were unmapped using `munmap` and the size of memory to allocate fits in it, it will use this as mapped address.
- If ASLR(Address Space Layout Randomization) flag is setup, there is randomization applied in finding memory gap.

However, in MIPS, it is assumed that newly mapped memory always exists at the last of the heap:
```solidity
if (a0 == 0) {
    v0 = state.heap;
    state.heap += sz;
} else {
    v0 = a0;
}
```

`mmap` does not guarantee that mapped memory exists in heap, it can be anywhere in virtual memory.
Also based on above facts, the resulting `$v0` might have different value between actual run-time and in MIPS, which will result in incorrect game resolution.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Mitigation is not 100% clear because it depends on run-time how memory is managed.


## Assessed type

Context
Invalid DISPUTED_L2_BLOCK_NUMBER is passed to VM
Audit: 2024-07-optimism-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol#L453


# Vulnerability details

## Impact
The span of the game tree at split depth is far larger than the length between the starting block and the claimed block, when `starting block + trace index + 1 > claimed block`, honest party should continue to commit to the root of the claimed block. However, the DISPUTED_L2_BLOCK_NUMBER passed to the VM is always `starting block + trace index + 1`, which means the op-program (at inter-block perspective) will not stop until it reached the l2 safe head(corresponding to parenthash), and if the claimed block is earlier than the safe head, it can be challenged and will be considered invalid.

## Proof of Concept
Since op-program is out of scope of this contest, this report will not spend too much time in proving *block earlier than safe head can be challenged*, instead it will only show the inconsistency within the smart contract part.

For simplicity, we assume the span of the game tree at split depth is 8 and starting block is 0. At block level, we use **defend a ->p b** to refer to commit a valid VM trace with a as starting output, b as disputed output, p as disputed block number and VALID as the final state, similarly **attack a ->p b** refers to commit a valid VM trace with ... and INVALID or PANIC as the final state. We use Bi to refer to the valid L2 root at block i.

Suppose Alice made a valid root claim B2 for block 2, Bob made a valid root claim B3 for block 3 and they claim at the same L1 block (so the stored l1Head will be the identical). Ideally, both of them should be able to defend their claim. We already know that Bob can **defend B2 ->3 B3** in his own game. What if Bob tries to attack Alice in her game? 

(Recall Alice's view of valid state is 12222222 and Bob's is 12333333)
1. Bob attacks by claiming B3. (trace index 3)
2. Alice attacks by claiming B2. (trace index 1)
3. Bob defends by claiming B3. (trace index 2)
4. Alice **attacks B2 ->3 B3**. (disputed block = starting block + trace index + 1 = 3)
```
    /// @inheritdoc IFaultDisputeGame
    function addLocalData(uint256 _ident, uint256 _execLeafIdx, uint256 _partOffset) external {
        // INVARIANT: Local data can only be added if the game is currently in progress.
        if (status != GameStatus.IN_PROGRESS) revert GameNotInProgress();

        (Claim starting, Position startingPos, Claim disputed, Position disputedPos) =
            _findStartingAndDisputedOutputs(_execLeafIdx);
        Hash uuid = _computeLocalContext(starting, startingPos, disputed, disputedPos);

        IPreimageOracle oracle = VM.oracle();
        if (_ident == LocalPreimageKey.L1_HEAD_HASH) {
            // Load the L1 head hash
            oracle.loadLocalData(_ident, uuid.raw(), l1Head().raw(), 32, _partOffset);
        } else if (_ident == LocalPreimageKey.STARTING_OUTPUT_ROOT) {
Some preimages to precompiles cannot be proven using `loadPrecompilePreimagePart()` 
Audit: 2024-07-optimism-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L195
https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L335


# Vulnerability details

The `PreimageOracle` contracts offers a streaming option for Keccak256 operations through the LPP submission process. As described in the Optimism [specs](https://specs.optimism.io/fault-proof/stage-one/fault-dispute-game.html?highlight=large%20preimage%20proposal#preimageoracle-interaction), "in the event that the preimage is too large to be submitted through calldata in a single block, challengers must resort to the streaming option".

However, the lack of a similar streaming option for other precompiles that accept large inputs, like SHA256, RIPEMD and the identity precompile will cause calls to these precompile contracts with larger inputs to not be provable on L1.

In the PoC below, we show how this can be exploited to create a L2 transaction that, to be proved, requires submission of a SHA256 preimage that isn't feasible in L1 due to the constrained transaction size.

While these precompiles may not currently be used by out-of-scope parts of the codebase, the in-scope contracts pose no limitations on what precompile are meant to be accessible in this manner (can be accelerated).

## Impact
It is possible to craft transactions on L2 that can't be proven on L1. When this happens, a honest claim about the execution of these transactions can't be defended and will consequently risk an unfair loss of a fault dispute game on L1.

## Proof of Concept
The below PoC shows how:
- On L2, a we have a contract that feeds a large amount of data to the SHA256 precompile
- Under conditions that are somewhat specific but at the same time easy to create intentionally:
  - the transaction triggering the SHA sum can consume little enough gas to be executed on L2
  - the SHA preimage can be too large to fit an L1 transaction 

<details>
  <summary>Coded PoC (Foundry)</summary>

```Solidity
pragma solidity ^0.8.0;

import "forge-std/Test.sol";

contract Hack is Test {
    function testL2Stream() public {
        uint preimageSize = 400_000;

        LargeHashContract sc = new LargeHashContract();
        bytes memory cdata = abi.encodeWithSelector(LargeHashContract.go.selector, preimageSize, block.timestamp);

        uint beforeGas = gasleft();
        // this transaction...
        address(sc).call(cdata);
        uint gasUsed = beforeGas - gasleft();

        // ...is easily feasible on L2...
        assertLe(cdata.length, 1e3);
        assertLe(gasUsed, 1e6);

        // ...but its SHA256 preimage can't be submitted
        // to PreimageOracle on L1 without streaming
        assertGt(preimageSize, 128e3);
    }
}

contract LargeHashContract {
    bytes32 public sha;

    function go (uint len, uint salt) exter
LPP metadata can be altered after the challenge period is over, allowing incorrect states to be proven
Audit: 2024-07-optimism-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L417
https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L431


# Vulnerability details

It was reported [in the Spearbit review (5.2.5 "Preimage proposals can be initialized multiple times")](https://github.com/spearbit/portfolio/blob/35e01e225519242589ab83f6fad100db892bcfe9/pdfs/Spearbit-Coinbase-Security-Review-June-2024.pdf) that it is possible to re-initialize LPPs, at a loss for the caller, because the `initLPP` function doesn't check for the LPP to exist already.

If we look at the `initLPP()` function, however, we can see that there is another vulnerability that can be chained: at L431, the to-be-initialized `LPPMetadata` is unnecessarily read from storage instead of being initialized empty:

```Solidity
File: PreimageOracle.sol
417:     function initLPP(uint256 _uuid, uint32 _partOffset, uint32 _claimedSize) external payable {
418:         // The bond provided must be at least `MIN_BOND_SIZE`.
419:         if (msg.value < MIN_BOND_SIZE) revert InsufficientBond();
420: 
421:         // The caller of `addLeavesLPP` must be an EOA, so that the call inputs are always available in block bodies.
422:         if (msg.sender != tx.origin) revert NotEOA();
423: 
424:         // The part offset must be within the bounds of the claimed size + 8.
425:         if (_partOffset >= _claimedSize + 8) revert PartOffsetOOB();
426: 
427:         // The claimed size must be at least `MIN_LPP_SIZE_BYTES`.
428:         if (_claimedSize < MIN_LPP_SIZE_BYTES) revert InvalidInputSize();
429: 
430:         // Initialize the proposal metadata.
431:         LPPMetaData metaData = proposalMetadata[msg.sender][_uuid];
432:         proposalMetadata[msg.sender][_uuid] = metaData.setPartOffset(_partOffset).setClaimedSize(_claimedSize);
433:         proposals.push(LargePreimageProposalKeys(msg.sender, _uuid));
434: 
435:         // Assign the bond to the proposal.
436:         proposalBonds[msg.sender][_uuid] = msg.value;
437:     }
```

Because of this, L431 and L432 in fact allow to arbitrarily change an existing LPP's `partOffset` and `claimedSize` metadata, while leaving unchanged its other metadata, including its finalization `timestamp` if set.

Additionally, `squeezeLPP()` does not check that `bytesProcessed == claimedSize` since this check is already performed on finalization in `addLeavesLPP()`.

By exploiting these three issues, it is then possible that:
- An LPP is first created and populated using correct data
- It is finalized, and passes its challenge period
- After the challenge period is over, its `claimedSize` is changed to an arbitrary length by calling `initLPP` again
- Immediately after (without an opportunity for a challenge to happen), the LPP data is stored in `preimageParts` with a `squee
An attacker can take the bond away from the honest challenger if `FDG.CLOCK_EXTENSION` is less than `PreimageOracle.CHALLENGE_PERIOD`
Audit: 2024-07-optimism-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L657
https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol#L371-L382


# Vulnerability details

Large preimage proposals (LPP) allow submitters to prove that certain data is a fixed part of the large preimage that produces a specific Keccak-256 hash value. Since the preimage is large, the process of LPP finalization involves multiple transactions. Because the intermediate steps are not verified on-chain, LPP flow requires a challenge period during which the proposal can't be finalized. In another issue, I've demonstrated that it is possible to finalize prematurely, but here we'll assume that the LPP flow is not broken.

To demonstrate this issue, let's consider a scenario where an attacker makes a freeloader claim at the depth `MAX_GAME_DEPTH`. In the worst case, honest challengers will have `FDG.CLOCK_EXTENSION` time to counter this move by calling the `step` function. The issue arises if this call requires a finalized LPP proposal. As mentioned earlier, the process of LPP finalization is not immediate and requires `PreimageOracle.CHALLENGE_PERIOD` to pass. If the `FDG.CLOCK_EXTENSION` is less than `PreimageOracle.CHALLENGE_PERIOD`, honest challengers will be unable to counter the freeloader claim since they cannot finalize the LPP proposal before the clock expires. This essentially means that a malicious actor can take the bond away from the honest challenger.

If we consider the current values of these parameters on mainnet, we'll see that this attack is possible with the current configuration:
```
CHALLENGE_PERIOD=86400 
CLOCK_EXTENSION=10800
```

## Impact
An attacker can take the bond away from the honest challenger.

## Proof of Concept
\-

## Tools Used
Manual Review

## Recommended Mitigation Steps
Ensure that honest challengers have enough time to finalize LPP to counter freeloader claims.





## Assessed type

Timing
L2 precompile calls can be impossible to reproduce on L1
Audit: 2024-07-optimism-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L335-L389


# Vulnerability details


## Impact

When certain precompiles are invoked in an L2 transaction, the OP fault proof program substitutes their logic with "type 6" preimage oracle queries. Reviewing the [op-program code](https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/op-program/client/l2/engineapi/precompiles.go#L52-L66) shows that the ecrecover (0x1), bn256Pairing (0x8), and kzgPointEvaluation (0xa) precompile addresses are currently part of this "precompile accelerators" system. For this system to function correctly, a precompile call on L2 must always be reproducible on L1 via the `loadPrecompilePreimagePart()` function in the `PreimageOracle`. This is currently not guaranteed in at least one scenario on OP mainnet.

Specifically, note that the bn256Pairing precompile has an unbounded gas cost of `34_000 * k + 45_000`, where `k` is the input size divided by 192. This means that a user can call the bn256Pairing precompile on L2 with an input that uses nearly the entire L2 block gas limit. Currently, the block gas limit on both Ethereum mainnet and OP mainnet is `30_000_000`. Since the block gas limits are the same, every precompile call on L2 is theoretically reproducible on L1 in isolation. However, since the `loadPrecompilePreimagePart()` function has its own overhead gas costs, it can be shown that some L2 precompile calls can be impossible to reproduce for the purposes of the `PreimageOracle`.

For example, consider a scenario where the following smart contract is successfully called on L2:

```solidity
pragma solidity 0.8.18;

contract L2GasLimit {
    fallback() external {
        assembly {
            let success := staticcall(
                gas(),
                0x08,
                0x80,
                165504,
                0x0,
                0x0
            )
            if iszero(success) { revert(0, 0) }
        }
    }
}
```

Since the input is `165_504` bytes, the cost of calling the bn256Pairing precompile is `29_353_000` gas. However, since [EIP-150](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md) limits the amount of gas the `PreimageOracle` can forward to `63/64` of its available gas, the `loadPrecompilePreimagePart()` function would require `29_353_000 * 64 / 63 = 29_818_920` gas in order to reproduce this precompile call successfully. There are two options for a user to attempt to meet this threshold:

1. The user calls `loadPrecompilePreimagePart()` from an EOA. Note that the input passed to the precompile needs to be provided as an argument:

    ```solidity
    function loadPrecompilePreimagePart(uint256 _partOffset, address _precompile, bytes calldata _input) external {
        // ...
    }
    ```
    
    Since the user spends 4 gas for every zero byte of calldata, this means t
`squeezeLPP()` can be called on unfinalized proposals
Audit: 2024-07-optimism-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L640-L657


# Vulnerability details

## Impact

In the `PreimageOracle`, the `squeezeLPP()` function is the last step of a large preimage proposal. In this final step, the last merkle leaf of the proposal is absorbed + squeezed to compute the final digest, which is then used for updating the oracle storage values. For someone to call `squeezeLPP()`, there are two checks on the proposal: 

1. A check that no one has countered the proposal by proving it is malicious:

    ```solidity
    if (metaData.countered()) revert BadProposal();
    ```
    
2. A check that the proposal has been finalized for `CHALLENGE_PERIOD` seconds:


    ```solidity
    if (block.timestamp - metaData.timestamp() <= CHALLENGE_PERIOD) revert ActiveProposal();
    ```


There is a mistake with the logic of the second check. If the proposal was never finalized in the first place, the `metaData.timestamp()` value would still be zero, which will trivially pass the timestamp difference check.

This means that an attacker can instantly create a large proposal, partially fill the proposal, and then call `squeezeLPP()` to update the storage mappings. The resulting digest will not match what would be expected from the proposal initialization, and the `proposalParts` may or may not be set at the time of `squeezeLPP()`. All values will be stored as if the proposal was a success.

It is easy to see how this would break the fault dispute game. For example, this would allow changing other users' previously stored preimages (see PoC below), which would lead to users timing-out before they can successfully call `step()` as they expect. 

## Proof of Concept

The following test can be added to `packages/contracts-bedrock/test/cannon/PreimageOracle.t.sol`:

```solidity
function test_squeeze_not_finalized_bug() public {
    /**********************************************************************
    1. Call the existing test function. For the PoC we will show that we can
    instantly change the values that the other test stored.
    **********************************************************************/
    test_squeeze_challengePeriodPassed_succeeds();

    /**********************************************************************
    2. Set up our malicious proposal. We will use the padded version 
    of the other test's data so we can have the same digest
    **********************************************************************/
    bytes memory otherTestData = new bytes(136);
    for (uint256 i; i < otherTestData.length; i++) otherTestData[i] = 0xFF;
    bytes memory attackerData = LibKeccak.padMemory(otherTestData);
    uint256 attackerUUID = TEST_UUID + 1;

    oracle.initLPP{ value: oracle.MIN_BOND_SIZE() }(attackerUUID, 0, uint32(attackerData.length));

    LibKeccak.StateMatrix memory matrix;
    {
        bytes32[] mem
The person who submits a large proposal can safely get their bond back at the beginning
Audit: 2024-07-optimism-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L419
https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L605
https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L654
https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L657
https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L544
https://github.com/code-423n4/2024-07-optimism/blob/70556044e5e080930f686c4e5acde420104bb2c4/packages/contracts-bedrock/src/cannon/PreimageOracle.sol#L791


# Vulnerability details

## Impact
When proposing a `large preimage`, `proposers` must provide `bonds`. 
These `bonds` can either be given to `challengers` during the `challenge period` or returned to the `proposer` if there are no `challenges`.
The `bonds` should not be returned to the `proposer` until the `challenge period` has ended. 
This is because the `bonds` act as an incentive for `challengers` to identify incorrect `proposals`.
However, if `proposers` can retrieve their `bonds` at any time, it removes the `incentive` for `challengers` to detect incorrect `proposals`, which poses a `high impact` and is very likely to happen.
## Proof of Concept
When `proposers` want to submit a new `large preimage`, they must provide a predefined `bond`. 
```
function initLPP(uint256 _uuid, uint32 _partOffset, uint32 _claimedSize) external payable {
    if (msg.value < MIN_BOND_SIZE) revert InsufficientBond();  // @audit, here
    ...
    proposalBonds[msg.sender][_uuid] = msg.value;
}
```
If `challengers` find incorrect commitments, the `bond` is sent to the `challengers`. 
```
function challengeLPP(
    address _claimant,
    uint256 _uuid,
    LibKeccak.StateMatrix memory _stateMatrix,
    Leaf calldata _preState,
    bytes32[] calldata _preStateProof,
    Leaf calldata _postState,
    bytes32[] calldata _postStateProof
)
    external
{
    ...
    // Mark the keccak claim as countered.
    proposalMetadata[_claimant][_uuid] = proposalMetadata[_claimant][_uuid].setCountered(true);  

    // Pay out the bond to the challenger.
    _payoutBond(_claimant, _uuid, msg.sender); // @audit, here
}
```
And `proposers` cannot `squeeze` their `proposals`. 
```
function squeezeLPP(
    address _claimant,
    uint256 _uuid,
    LibKeccak.StateMatrix memory _stateMatrix,
    Leaf calldata _preState,
    bytes32[] calldata _preStateProof,
    Leaf calldata _postState,
    bytes32[] calldata _postStateProof
)
    external
{
    LPPMetaData metaData = proposalMetadata[_claimant][_uuid];

    // Check if the proposal was countered.
    if