xanoutas/solidity-security-findings huggingface dataset:
[Tomo-M1] Multiple initializations
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L71-L103
https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1EthBridge.sol#L56-L80


# Vulnerability details

[https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L71-L103](https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L71-L103)

```solidity
function initialize(
        bytes[] calldata _factoryDeps,
        address _l2TokenFactory,
        address _governor
    ) external reentrancyGuardInitializer {
        // We are expecting to see the exect two bytecodes that are needed to initiailize the bridge
        require(_factoryDeps.length == 2, "mk");
        l2ProxyTokenBytecodeHash = L2ContractHelper.hashL2Bytecode(_factoryDeps[1]);
        l2TokenFactory = _l2TokenFactory;

        bytes32 create2Salt = bytes32(0);
        bytes memory create2Input = abi.encode(address(this), l2ProxyTokenBytecodeHash, _governor);
        bytes32 l2BridgeBytecodeHash = L2ContractHelper.hashL2Bytecode(_factoryDeps[0]);
        bytes memory deployL2BridgeCalldata = abi.encodeCall(
            IContractDeployer.create2,
            (create2Salt, l2BridgeBytecodeHash, create2Input)
        );

        l2Bridge = L2ContractHelper.computeCreate2Address(
            address(this),
            create2Salt,
            l2BridgeBytecodeHash,
            keccak256(create2Input)
        );

        zkSyncMailbox.requestL2Transaction(
            DEPLOYER_SYSTEM_CONTRACT_ADDRESS,
            0,
            deployL2BridgeCalldata,
            DEPLOY_L2_BRIDGE_COUNTERPART_ERGS_LIMIT,
            _factoryDeps
        );
    }
```

[https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L282-L287](https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L282-L287)

```solidity
function l2TokenAddress(address _l1Token) public view returns (address) {
        bytes32 constructorInputHash = keccak256(abi.encode(address(l2TokenFactory), ""));
        bytes32 salt = bytes32(uint256(uint160(_l1Token)));

        return L2ContractHelper.computeCreate2Address(l2Bridge, salt, l2ProxyTokenBytecodeHash, constructorInputHash);
    }
}
```

The `initialize()` set some important values.

For example, if the value of `l2TokenFactory` changes, the return value of `l2TokenAddress()`will be changed.
Therefore, the `initialize()` should call only once but, this `initialize` function can call multiple times.

## Impact

The `initatialize()` can call multiple times, it happens the unexpected error

## Proof of Concept

[https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L71-L103](https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L71-L103)

[https://github.com/code-423n4/2022-10-z
Variable `message ` does not return value To L1 Bridge
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/zksync/contracts/bridge/L2ERC20Bridge.sol#L87-L101


# Vulnerability details

## Impact

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/zksync/contracts/bridge/L2ERC20Bridge.sol#L98

The balance could become insufficient at the time of `message`. User’s fund will be locked due to DoS. The way to take the fund out is to send more token into the contract, causing fund loss to the bridge protocol

## Proof of Concept
In `withdraw`, but it is possible that as time goes on, the value become smaller and fail the transfer. As a result, the withdraw() call will revert, causing DoS, and lock user’s fund.

```
        address l1Token = l1TokenAddress[_l2Token];
        require(l1Token != address(0), "yh");
        bytes memory message = _getL1WithdrawMessage(_l1Receiver, l1Token, _amount);
        L2ContractHelper.sendMessageToL1(message);
```
## Tools Used
VSCode

## Recommended Mitigation Steps
Make sure that all returned `message` values from the function call are used.
M01: RETURN VALUES OF TRANSFER()/TRANSFERFROM() NOT CHECKED
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L142
https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L214
https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L254


# Vulnerability details

### M01: RETURN VALUES OF TRANSFER()/TRANSFERFROM() NOT CHECKED
#### prof
TRANSFER()/TRANSFERFROM() NOT CHECKED
Not all IERC20 implementations revert() when there’s a failure in transfer()/transferFrom(). The function signature has a boolean return value and they indicate errors that way instead. By not checking the return value, operations that should have marked as failed, may potentially go through without actually making a payment
#### problem
bridge/L1ERC20Bridge.sol, 142, b'        _token.safeTransferFrom(_from, address(this), _amount);'
bridge/L1ERC20Bridge.sol, 214, b'        IERC20(_l1Token).safeTransfer(_depositSender, amount);'
bridge/L1ERC20Bridge.sol, 254, b'        IERC20(l1Token).safeTransfer(l1Receiver, amount);'
_resetProposal() doesnt actually work
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/zksync/facets/DiamondCut.sol#L126-L135


# Vulnerability details

## Impact
the function _resetProposal() doesn't actually reset proposals to default values, which means if a proposal needs to be changed or discarded can actually be executed 

## Proof of Concept
\\\diff
diff --git a/./diamond_cut_test.ts.orig b/./diamond_cut_test.ts
index 12882e3..4d4a718 100644
--- a/./diamond_cut_test.ts.orig
+++ b/./diamond_cut_test.ts
@@ -425,6 +425,7 @@ describe('Diamond proxy tests', function () {
             ];
 
             await proxyAsDiamondCut.proposeDiamondCut(facetCuts, ethers.constants.AddressZero);
+            await proxyAsDiamondCut.cancelDiamondCutProposal()
             await proxyAsDiamondCut.executeDiamondCutProposal(
                 diamondCut(facetCuts, ethers.constants.AddressZero, '0x')
             );
.

## Tools Used
hardhat
## Recommended Mitigation Steps
the function should accept certain proposal parameters which can be reset to their default values or at least have the whole proposal deleted
Miss check if timestamp of current L2 block is greater than the previous one
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/zksync/facets/Executor.sol#L23-L78


# Vulnerability details

## Impact
Block timestamps should be forced to always grow, function '_commitOneBlock' is missing the check.

## Proof of Concept
https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/zksync/facets/Executor.sol#L23-L78

## Tools Used
VS Code

## Recommended Mitigation Steps
```
function _commitOneBlock(StoredBlockInfo memory _previousBlock, CommitBlockInfo calldata _newBlock)
   // ...
{
    require(_newBlock.timestamp >= _previousBlock.timestamp, "tt"); 
    // ...
}
```
Reentrancy risk of L2ETHBridge
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/zksync/contracts/bridge/L2ETHBridge.sol#L52


# Vulnerability details

## Impact
As implementations of L2 system contracts are not public yet, here i assume ETH_TOKEN_SYSTEM_CONTRACT works like EVM while sending bridged ETH: if the receiver is a contract address, then the contract's fallback() will be called .

If the above condition is true, there is a Reentrancy problem on 'finalizeDeposit' and 'withdraw'.

## Proof of Concept
As shown below, if the an attacker call 'withdraw' by reentrance, then event 'FinalizeDeposit' will be triggered later than 'WithdrawalInitiated' on log history. It may cause dApps which rely on these logs can't work properly. In dApp view account balance changes like this: 0 ETH => -100 ETH => +100 ETH

```
function finalizeDeposit(
   // ...
) external {
   // ...

    ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS.bridgeMint(_l2Receiver, _amount); // @audit step 1

    emit FinalizeDeposit(_l1Sender, _l2Receiver, CONVENTIONAL_ETH_ADDRESS, _amount); @audit step 4
}

function withdraw(
    // ...
) external override {
   // ...

    ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS.bridgeBurn(msg.sender, _amount); // @audit step 2
   // ...

    emit WithdrawalInitiated(msg.sender, _l1Receiver, CONVENTIONAL_ETH_ADDRESS, _amount); // @audit step 3
}
```

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/zksync/contracts/bridge/L2ETHBridge.sol#L41-L55
https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/zksync/contracts/bridge/L2ETHBridge.sol#L59-L71

## Tools Used
VS Code

## Recommended Mitigation Steps
Add reentrance protection on  'finalizeDeposit' and 'finalizeDeposit'.
Assign owner directly
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/common/AllowList.sol#L32-L35


# Vulnerability details

## Impact
Assign owner/admin and etc directly is bad practice.

## Proof of Concept
https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/common/AllowList.sol#L32-L35

## Tools Used
Code reading

## Recommended Mitigation Steps
Implement propose & claim pattern.
Edit: This contract already have setPendingOwner and acceptOwner methods. So we can replace `owner = _owner` line in constructor with setPendingOwner(_owner)
use reentrancyGuardInitializer two times in one contract
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L57
https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L75


# Vulnerability details

## Impact
Depending on the implementation, this can lead to vulnerabilities.

## Proof of Concept
https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L57
https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L75

## Tools Used
Static analtycs

## Recommended Mitigation Steps
Use `reentrancyGuardInitializer` modifier one time
Failed transfer with low level call won't revert
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1EthBridge.sol#L232-L239


# Vulnerability details

### Description
`L1EthBridge.sol#_withdrawFunds()` uses inline assembly `call` to transfer ether from contract to receiver. According to <a href="https://docs.soliditylang.org/en/develop/control-structures.html#error-handling-assert-require-revert-and-exceptions)">Solidity Docs</a> the `call` may return `true` even if it was a failure due to non-existing contract. This may result in ether being lost as the withdrawal is now considered finalized. 

### Proof of concept

```
function _withdrawFunds(address _to, uint256 _amount) internal {
        bool callSuccess;
        // Low-level assembly call, to avoid any memory copying (save gas)
        assembly {
            callSuccess := call(gas(), _to, _amount, 0, 0, 0, 0)
        }
        require(callSuccess);
    }
```

https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1EthBridge.sol#L232-L239

### Recommended Mitigation Steps

Check for contract existence by adding and checking the `codehash`:
```
codehash := extcodehash(_to)
```
Incorrect ERC20 Interface observed in `DummyERC20NoTransferReturnValue.sol`
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/dev-contracts/test/DummyERC20NoTransferReturnValue.sol#L6


# Vulnerability details

## Impact
The contract was found to be using ERC20 interfaces. The implementations were improperly done and do not follow the standard ERC20 implementation. Either the return values or the parameter type was missing or incorrect. This will cause errors when the function that called these interfaces expects some return value but instead, nothing or the default values will be returned.

The following ERC20 function was found to be non-compliant - `transfer`

## Issue Details
The ERC20 Token standard has a specific format for all the functions. In Solidity, a function selector is derived from its function name and the type of the input parameters.

So, if the return value is omitted from function description, function selector will still be ERC20 compliant but the actual function won’t be. 

## Remediation
Make sure that the functions used by the contract is ERC20 compliant and follows the same format with respect to the arguments passed and the return values expected so that it remains compliant with the token standard.
Incorrect ERC20 Interface observed in `DummyERC20BytesTransferReturnValue.sol`
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/dev-contracts/test/DummyERC20BytesTransferReturnValue.sol#L12-L18


# Vulnerability details

## Impact
The contract was found to be using ERC20 interfaces. The implementations were improperly done and do not follow the standard ERC20 implementation. Either the return values or the parameter type was missing or incorrect. This will cause errors when the function that called these interfaces expects some return value but instead, nothing or the default values will be returned.

The following ERC20 function was found to be non-compliant - `transfer`

## Issue Details
The ERC20 Token standard has a specific format for all the functions. In Solidity, a function selector is derived from its function name and the type of the input parameters.

So, if the return value is omitted from function description, function selector will still be ERC20 compliant but the actual function won’t be. 

## Remediation
Make sure that the functions used by the contract is ERC20 compliant and follows the same format with respect to the arguments passed and the return values expected so that it remains compliant with the token standard.
Solidity Modifier `reentrancyGuardInitializer` side effects in `ReentrancyGuard.sol`
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/common/ReentrancyGuard.sol#L38-L41


# Vulnerability details

## Impact
The contract `ReentrancyGuard` was found to be violating this pattern and the modifier `reentrancyGuardInitializer` was making sensitive state changes and modifications.

## Vulnerability Details
Solidity functions should always use the Checks-Effects-Interactions pattern which states that the initial stage will contain only checks and validations which resides in the modifiers.
Due to this reason, modifiers should only implement checks and validations inside of it and should not make state changes and external calls. 

Vulnerable code:
```
    modifier reentrancyGuardInitializer() {
        _initializeReentrancyGuard();
        _;
    }
```

## Remediation
Only use modifiers for implementing checks and validations. Do not make external calls or state changing actions inside modifiers.
Solidity Modifier `nonReentrant` side effects in `ReentrancyGuard.sol`
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/common/ReentrancyGuard.sol#L65-L86


# Vulnerability details

## Impact
The contract `ReentrancyGuard` was found to be violating this pattern and the modifier `nonReentrant` was making sensitive state changes and modifications.

## Vulnerability Details
Solidity functions should always use the Checks-Effects-Interactions pattern which states that the initial stage will contain only checks and validations which resides in the modifiers.
Due to this reason, modifiers should only implement checks and validations inside of it and should not make state changes and external calls. 

Vulnerable code:
```
    modifier nonReentrant() {
        uint256 _status;
        assembly {
            _status := sload(LOCK_FLAG_ADDRESS)
        }

        // On the first call to nonReentrant, _notEntered will be true
        require(_status == _NOT_ENTERED, "r1");

        // Any calls to nonReentrant after this point will fail
        assembly {
            sstore(LOCK_FLAG_ADDRESS, _ENTERED)
        }

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        assembly {
            sstore(LOCK_FLAG_ADDRESS, _NOT_ENTERED)
        }
    }
```

## Remediation
Only use modifiers for implementing checks and validations. Do not make external calls or state changing actions inside modifiers.
Solidity Modifier `senderCanCallFunction` side effects in `AllowListed.sol`
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/common/AllowListed.sol#L9-L18


# Vulnerability details

## Impact
The contract `AllowListed.sol` was found to be violating this pattern and the modifier `senderCanCallFunction` was making sensitive state changes and modifications.

## Vulnerability details
Solidity functions should always use the Checks-Effects-Interactions pattern which states that the initial stage will contain only checks and validations which resides in the modifiers.
Due to this reason, modifiers should only implement checks and validations inside of it and should not make state changes and external calls.

Vulnerable code:
```
    modifier senderCanCallFunction(IAllowList _allowList) {
        // Preventing the stack too deep error
        {
            // Take the first four bytes of the calldata as a function selector.
            // Please note, `msg.data[:4]` will revert the call if the calldata is less than four bytes.
            bytes4 functionSig = bytes4(msg.data[:4]);
            require(_allowList.canCall(msg.sender, address(this), functionSig), "nr");
        }
        _;
    }
```

## Remediation
Only use modifiers for implementing checks and validations. Do not make external calls or state changing actions inside modifiers.
Unbounded L2 Logs can lead to DoS
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/5a31c9db8ab32175dbd7264b05ce84931b6c0428/ethereum/contracts/zksync/facets/Executor.sol#L111


# Vulnerability details

## Impact
`_processL2Logs` (that is called within `_commitOneBlock`, which is called from `commitBlocks`) iterates over `emittedL2Logs` which does not have an upper bound in the smart contract:
```solidity
for (uint256 i = 0; i < emittedL2Logs.length; i = i.uncheckedAdd(L2_TO_L1_LOG_SERIALIZE_SIZE)) {
...
}
```
Therefore, if the log length for a block is very long (or rather the number of log entries is very large, as they have a fixed size), this operation may run out of gas. The consequences of that for ZKSync are very severe. No more commitments are possible, because they need to be done consecutively.

## Proof Of Concept
Let's say that because of a sudden increase in usage of ZKSync, there were many L2 logs for one particular block $b$. Because of that, when `commitBlocks` is called with this block, it always runs out of gas, as `_processL2Logs` iterates over all log entries. Therefore, it is not possible to do a commitment for block $b$. However, it is also not possible to commit blocks with numbers greater than $b$, as block $b$ needs to be comimtted first. In such a situation, no more L2 -> L1 communication is possible.

## Recommended Mitigation Steps
Consider allowing to perform the commitment in multiple transactions (with the intermediate state stored) in case there are a lot of L2 logs.
Executor: Blocks may be uncommittable after L1 unavailability
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/5a31c9db8ab32175dbd7264b05ce84931b6c0428/ethereum/contracts/zksync/facets/Executor.sol#L50


# Vulnerability details

## Impact
`Executor._commitOneBlock` checks that the L2 timestamp of a block is not older than `COMMIT_TIMESTAMP_NOT_OLDER`:
```solidity
bool timestampNotTooSmall = block.timestamp - COMMIT_TIMESTAMP_NOT_OLDER <= l2BlockTimestamp;
```
This can become very problematic when there is a temporary failure to produce new blocks on Ethereum for some time. While this is (fortunately) not a common occurence, it can happen, which previous incidents like the Shanghai DoS attacks or the Berlin consensus failure demonstrate. When such a failure lasts longer than `COMMIT_TIMESTAMP_NOT_OLDER`, the oldest non-committed block cannot be committed. Furthermore, all blocks after that one can also not be committed, because they need to be committed sequentially.
The consequences of this are very severe. The whole ZKSync system will be bricked and bridged funds cannot be transferred back to Ethereum.

Of course, this problem can also occur if validators are down for an extended period of time (longer than `COMMIT_TIMESTAMP_NOT_OLDER`).

## Proof Of Concept
Let's say Ethereum fails to produce new blocks for some time $t$ because of a vulnerability in the consensus mechanism that is very hard to detect and fix. We assume that $t$ > `COMMIT_TIMESTAMP_NOT_OLDER`, which can happen in practice, because `COMMIT_TIMESTAMP_NOT_OLDER` is a configurable value, so I do not make any assumptions about its size (as there are no restrictions for the value in the smart contract). At timestamp $t + 1$, a block is sealed and should be committed to L1. However, this is not possible and only becomes possible again at a timestamp where it is no longer allowed. Because all blocks need to be committed sequentially, all commitments of blocks with a higher number also fail, i.e. the system becomes disfunctional.

## Recommended Mitigation Steps
Introduce a recovery mechanism for such scenarios, e.g. still allowing older blocks under some circumstances (when it was allowed by governance).