xanoutas/solidity-security-findings huggingface dataset:
Multi Step upgrades introduce security risk
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/zksync/facets/DiamondCut.sol#L68


# Vulnerability details

## Impact
If Governor upgrades the system using `executeDiamondCutProposal` function then freezing is removed. This could be a problem where Governor was still not ready to make freezable facet available as shown in POC

## Proof of Concept
1. Governor discovers a security flaw in one of the Facet A
2. To prevent any exploitation, freeze the Diamond using `emergencyFreezeDiamond` function

```solidity
function emergencyFreezeDiamond() external onlyGovernor {
        Diamond.DiamondStorage storage diamondStorage = Diamond.getDiamondStorage();
        ...

        emit EmergencyFreeze();
    }
```

3. Governor realizes that upgrade requires 2 step:

a. First step with custom function to tweak the variable state to correct one (custom facet being unfreezable and Governor callable only)
b. Second step with revised code

4. Governor upgrades the system with Step 3.a which deploys the new custom code

5. But this function also unfreezes the diamond, meaning attacker can now attack the vulnerable code piece before Governor can move to step 3.b

## Recommended Mitigation Steps
Remove the lines and let Governor manually unfreeze system:

```diff
- if (diamondStorage.isFrozen) {
-            diamondStorage.isFrozen = false;
-            emit Unfreeze(s.diamondCutStorage.lastDiamondFreezeTimestamp);
-        }
```
Funds can be stolen
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/main/zksync/contracts/bridge/L2ERC20Bridge.sol#L58


# Vulnerability details

## Impact
For L2, any deposit finalization should come from L1 bridge address. Now the MailBox contract is currently helping to simulate such calls to be coming from L1. The problem is if within zkSync ecosystem, a contract is deployed with address same as L1 bridge address then this internal contract will be able to control the L2 bridge. 

This can be possible under 2 scenario:

1. Since zkSync internals are still unknown (System contract code are still not public "https://v2-docs.zksync.io/dev/developer-guides/contracts/system-contracts.html#bootloader"),  any attack later once code is public could allow getting this address
2. If there are any future plans to support custom contract address

## Proof of Concept
1. Lets say L1 bridge is currently deployed on ETH at address 0xabc...
2. This bridge now deploys L2 bridge which is calculated at address 0xdef... using [initialize](https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L89)

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

3. Now deposit finalization on L2 can only happen when call is coming from 0xabc... and there is no check to see if call is coming from Ethereum or directly from zkSync

4. Since zkSync has custom contract creation code and its internal code is still not public, in future possible issue on zkSync may allow an attacker to gain 0xabc... address on zkSync environment. 

5. Now Attacker can simply call the L2 bridge and mint as many token as he wants which he can later redeem using the genuine L1 bridge. The call will not fail since its coming from 0xabc and it is not checked whether call is coming from ETH or not


## Recommended Mitigation Steps
Initialize a contract on zksync side with same l1bridge address which nullify any such scenario
Also it would be good to add a check id or domain check confirming if call is coming from correct environment.
Asymmetric level of transaction validation between L1 side and L2. 
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/bridge/L1ERC20Bridge.sol#L225
https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/bridge/L1ERC20Bridge.sol#L232
https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/bridge/L1ERC20Bridge.sol#L179


# Vulnerability details

## Impact
May have double counting of deposits?

## Proof of Concept
On L1 side, there are validations that ensure funds don't get lost nor double counted.
For example, [`finalizeWithdrawal`](https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/bridge/L1ERC20Bridge.sol#L225) may be called multiple times by allowed callers in case previous calls fail somehow. [`isWithdrawalFinalized[_l2BlockNumber][_l2MessageIndex]`](https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/bridge/L1ERC20Bridge.sol#L232) then guarantees that only one of them will succeed.
Also, if a deposit failed somehow, [`claimFailedDeposit`](https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/bridge/L1ERC20Bridge.sol#L179) may be called to recover the deposited funds.

However, on L2 side, such mechanism does not exist. We are basically completely trusting l1Bridge as the msg.sender to always succeed at the first time.

## Tools Used
VS Code

## Recommended Mitigation Steps
Wonder if there is a need to have something to this effect on the L2 side:
``isDepositFinalized[_l2BlockNumber][_l2MessageIndex]` or `cancelWithdraw(...)`
Add chainId to address creation.
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Although it may not be in the plan to deploy multiple zkSync rollups, it makes sense to include chainid in computeCreate2Address from the very start. It would be more EIP-155 compliant, and will prevent future replay attacks, where a tx of first zkSync rollup can be replayed on a 2nd rollup.

## Proof of Concept
When creating new rollup addresses, [`computeCreate2Address`](https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/common/L2ContractHelper.sol#L83) takes into account `CREATE2_PREFIX, senderBytes, _salt, _bytecodeHash, _constructorInputHash`, however, `chainId` is not part of the input.

[EIP-155](https://eips.ethereum.org/EIPS/eip-155) was created back in 2016 to provide simple replay attack protection. Even now, we still see such attacks due to non-EIP155 compliant contract addresses. Here is a recent case study of ["How 20 Million $OP Was Stolen from the Multisig Wallet (Not Yet) Owned by Wintermute"](https://inspexco.medium.com/how-20-million-op-was-stolen-from-the-multisig-wallet-not-yet-owned-by-wintermute-3f6c75db740a). If chainId were embedded in the address creation from the very beginning, we would stem it from ever happening to zkSync rollups.

## Tools Used
VS Code

## Recommended Mitigation Steps
Consider adding `chainId` as part of `computeCreate2Address` parameter list.
Aliasing L1 sender on L2
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/zksync/contracts/bridge/L2ERC20Bridge.sol#L58


# Vulnerability details

## Impact
If a contract on L1 has the same address as a contract on L2 but doesn't have the same code, we will have a critical issue, as funds will be lost.

## Proof of Concept
Both Optimism and Arbitrum implements `applyL1ToL2Alias / undoL1ToL2Alias` logic to prevent an attack in which a contract on L1 has the same address as a contract on L2 but doesn't have the same code.
Here is the link to Optimism's bedrock implementation.
The `applyL1ToL2Alias / undoL1ToL2Alias` functions are defined in [AddressAliasHelper.sol](https://github.com/ethereum-optimism/optimism/blob/4abae6184aae5957aeef2a0798da32c04645d745/packages/contracts-bedrock/contracts/vendor/AddressAliasHelper.sol#L28)
`applyL1ToL2Alias` is used in [OptimismPortal.sol](https://github.com/ethereum-optimism/optimism/blob/4abae6184aae5957aeef2a0798da32c04645d745/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L263) to initiate `depositTransaction`.
`undoL1ToL2Alias` is applied in [L2CrossDomainMessenger.sol](https://github.com/ethereum-optimism/optimism/blob/4abae6184aae5957aeef2a0798da32c04645d745/packages/contracts-bedrock/contracts/L2/L2CrossDomainMessenger.sol#L66) to recover the actual msg sender.

## Tools Used
VS Code

## Recommended Mitigation Steps
Consider if it makes sense for zkSync to incorporate such logic.
No incentive mechanism for allowed senders to call `finalizeWithdrawal` on behalf of withdrawers.
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/bridge/L1ERC20Bridge.sol#L225
https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/bridge/L1EthBridge.sol#L182


# Vulnerability details

## Impact
Since `finalizeWithdrawal` is a permissioned function, if there are no mechanism in place for allowed senders to trigger it, withdrawers would not able to receive their funds in a timely fashion.

## Proof of Concept
The withdrawal functions such as`claimFailedDeposit`,`finalizeWithdrawal` can only be called by permissioned callers in both [`L1ERC20Bridge`](https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/bridge/L1ERC20Bridge.sol#L225) and [`L1EthBridge`](https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/ethereum/contracts/bridge/L1EthBridge.sol#L182). If permissioned senders do not trigger these functions, withdrawers would not able to extract their own funds.

## Tools Used
VS Code

## Recommended Mitigation Steps
Implement a mechanism that incentives permissioned senders to make such calls in a timely manner.
Cross-chain replay attacks are possible with `create2()`
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/common/L2ContractHelper.sol#L19-L24


# Vulnerability details

### Impact
Mistakes made on one chain can be re-applied to a new chain

There is no `chain.id` in the `create2()` function data

If a user does `create2() ` using the wrong network, an attacker can replay the action on the correct chain, and steal the funds a-la the wintermute gnosis safe attack, where the attacker can create the same address that the user tried to, and steal the funds from there


https://mirror.xyz/0xbuidlerdao.eth/lOE5VN-BHI0olGOXe27F0auviIuoSlnou_9t3XRJseY



### Proof of Concept

```js
ethereum/contracts/common/L2ContractHelper.sol:
  18  
  19:     function create2(
  20:         bytes32 _salt,
  21:         bytes32 _bytecodeHash,
  22:         bytes calldata _input
  23:     ) external;
  24  }

zksync/contracts/L2ContractHelper.sol:
   9  interface IContractDeployer {
  10:     function create2(
  11:         bytes32 _salt,
  12:         bytes32 _bytecodeHash,
  13:         bytes calldata _input
  14:     ) external;
  15  }



```

### Tools Used
Manual Code Review

### Recommended Mitigation Steps
Include the `chain.id` in `create2()` function
Same function
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#L78
https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/zksync/contracts/bridge/L2ETHBridge.sol#L84


# Vulnerability details

Both functions do the same thing, while logically one of the functions should be for layer one and the other function should be in layer two to perform the same operation.


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


https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/zksync/contracts/bridge/L2ETHBridge.sol#L84
ergs price problem
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details



No mechanism has been defined for " l2TransactionBaseCost" function. On the other hand, any Oracle is not used in the whole project to get the price of gas, so this whole project will suffer in coordinating between the price of “ergs”  in  ZK layer and the real and momentary gas price in the Ethereum layer in the future.
L2ETHBridge.l2TokenAddress returns CONVENTIONAL_ETH_ADDRESS instead of ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS
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#L79-L81


# Vulnerability details

## Impact
In the L2ETHBridge contract, both the withdraw and l2TokenAddress functions use CONVENTIONAL_ETH_ADDRESS as the address of the L2ETH. However, L2ETH is actually an L2StandardToken contract at the ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS address.
If the user wants to use the l2TokenAddress function to get the L2ETH address to transfer tokens or check the balance, the call will fail because l2TokenAddress will return CONVENTIONAL_ETH_ADDRESS instead of ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS. 

## Proof of Concept
https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/zksync/contracts/bridge/L2ETHBridge.sol#L79-L81
https://github.com/code-423n4/2022-10-zksync/blob/4db6c596931a291b17a4e0e2929adf810a4a0eed/zksync/contracts/bridge/L2ETHBridge.sol#L59-L66
## Tools Used
None
## Recommended Mitigation Steps
Consider using ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS as the L2ETH address in the withdraw and l2TokenAddress functions of the L2ETHBridge contract.
Validator may refuse to process the cross-chain transaction.
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/zksync/facets/Executor.sol#L152
https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/zksync/facets/Executor.sol#L208
https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/zksync/facets/Executor.sol#L221
https://github.com/code-423n4/2022-10-zksync/blob/456078b53a6d09636b84522ac8f3e8049e4e3af5/ethereum/contracts/zksync/facets/Executor.sol#L336


# Vulnerability details

## Impact

Currently the project trust model is that the governor and the validator to not act maliciously.

But if the validator refuse to process the cross-chain transaction, the user's fund is locked infinitely in the L1ERC20Bridge.sol and in L1ETHBridge.sol

## Proof of Concept

In the current implementation, when user starts a cross-chain transfer, the user needs to call deposit and the cross-chain is put into a queue.

The validator needs to pop from the queue to process the job in first-in-first-out order.

However, if the validator just refuse to process the cross-chain transaction by calling the function that has the modifier onlyValidator in the Executor.sol. The transaction in the queue is stucked.

```solidity
function commitBlocks(StoredBlockInfo memory _lastCommittedBlockData, CommitBlockInfo[] calldata _newBlocksData)
	external
	override
	nonReentrant
	onlyValidator
{
```

and

```solidity
    function executeBlocks(StoredBlockInfo[] calldata _blocksData) external nonReentrant onlyValidator {
```

and 

```solidity
    function proveBlocks(
        StoredBlockInfo calldata _prevBlock,
        StoredBlockInfo[] calldata _committedBlocks,
        ProofInput calldata _proof
    ) external nonReentrant onlyValidator {
```

## Tools Used

VS Code

## Recommended Mitigation Steps

If the validator turns rogue, there is no cost for validator but the cost from the project and the cost from user is great. It is time confusing for governor and project to set up new validator infra.

I think a way to mitigate this to make sure the validator does the job is let them stake tokens and if they does not process transaction,

The protocol punish the validator by slashing their tokens.
[Tomo-M3] Can overwrite depositAmount
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#L107
https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L129


# Vulnerability details

https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/bridge/L1ERC20Bridge.sol#L129
```solidity
// Save the deposited amount to claim funds on L1 if the deposit failed on L2
depositAmount[msg.sender][_l1Token][txHash] = amount;
```

If users execute `deposit()` at the same time with the same parameters, users will lose their funds due to overwriting the `depositAmount`.

 

### Example

1. Tom execute `deposit()` as follows
`deposit(address(Tomy),address(WBTC),100)` 
2. Tom thought the above transaction he sent was failed, so he immediately ran it again with the same parameters
3. However, the first transaction has not failed
4. Therefore, Tom executed `deposit()` at the same time with the same parameters
5. The Tom’s total amount of `depositAmount` should be 200 WBTC but the txHash depends on parameters and `block.timestamp`, two txHashe will be the same
6. Finally, the `depositAmount` of Tom is set twice and he lost 100 WBTC

## Impact

Users lose their funds if calling a function at the same time with the same parameters

## Proof of Concept

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

```solidity
function deposit(
    address _l2Receiver,
    address _l1Token,
    uint256 _amount
) external payable nonReentrant senderCanCallFunction(allowList) returns (bytes32 txHash) {
    uint256 amount = _depositFunds(msg.sender, IERC20(_l1Token), _amount);
    require(amount > 0, "1T"); // empty deposit amount

    bytes memory l2TxCalldata = _getDepositL2Calldata(msg.sender, _l2Receiver, _l1Token, amount);
    txHash = zkSyncMailbox.requestL2Transaction{value: msg.value}(
        l2Bridge,
        0, // L2 msg.value
        l2TxCalldata,
        DEPOSIT_ERGS_LIMIT,
        new bytes[](0)
    );

    // Save the deposited amount to claim funds on L1 if the deposit failed on L2
    depositAmount[msg.sender][_l1Token][txHash] = amount;

    emit DepositInitiated(msg.sender, _l2Receiver, _l1Token, amount);
}
```

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

```solidity
/// @dev Generate a calldata for calling the deposit finalization on the L2 bridge contract
function _getDepositL2Calldata(
    address _l1Sender,
    address _l2Receiver,
    address _l1Token,
    uint256 _amount
) internal view returns (bytes memory txCalldata) {
    bytes memory gettersData = _getERC20Getters(_l1Token);

    txCalldata = abi.encodeCall(
        IL2Bridge.finali
Unintentional approvals may still happen
Audit: 2022-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/zksync/facets/DiamondCut.sol#L103-L122


# Vulnerability details

## Impact
Unintentional approvals may happen, including reorg attacks.

## Proof of Concept

A DiamondCutFacet upgrade proposal contains info of `_facetCuts` and `_initAddress`. In [`DiamondCutFacet.approveEmergencyDiamondCutAsSecurityCouncilMember`](https://github.com/code-423n4/2022-10-zksync/blob/main/ethereum/contracts/zksync/facets/DiamondCut.sol#L103-L122) the parameter `_diamondCutHash` already used to represent the propoal detail to prevent unintentional approvals(including reorg attacks).

In addition to containing these explicit info, an diamond upgrade proposal also implies an additional information, that is the current status of the contract(Diamond).

Different upgrades may have the same upgrade info(`_facetCuts` and `_initAddress`), which means the same `_diamondCutHash`, but, the contract state or diamond cuts may different.
This can lead to different results for these upgrades, and a security council member may have different ideas about these different upgrades with same `_diamondCutHash`.

Suppose there are three proposals A, B, C, and a council member M who sent transaction (`T`) to approve C with the desire of upgrade order `A -> B -> C`.
But the actual upgrade order on chain may be `A - C` or `A - D - C` which could cause `T` be an unintentional approval of M.

## Tools Used
VS Code

## Recommended Mitigation Steps

Add parameters `uint256 _proposedTimestamp, uint256 _proposalId` to `approveEmergencyDiamondCutAsSecurityCouncilMember` and require all parameters match.

```
diff --git a/ethereum/contracts/zksync/facets/DiamondCut.sol b/ethereum/contracts/zksync/facets/DiamondCut.sol
index 9a60aea..6f52e29 100644
--- a/ethereum/contracts/zksync/facets/DiamondCut.sol
+++ b/ethereum/contracts/zksync/facets/DiamondCut.sol
@@ -102,7 +102,7 @@ contract DiamondCutFacet is Base, IDiamondCut {

   /// @notice Gives another approval for the instant upgrade (diamond cut) by the security council member
   /// @param _diamondCutHash The hash of the diamond cut that security council members want to approve. Needed to prevent unintentional approvals, including reorg attacks
-    function approveEmergencyDiamondCutAsSecurityCouncilMember(bytes32 _diamondCutHash) external {
+    function approveEmergencyDiamondCutAsSecurityCouncilMember(bytes32 _diamondCutHash, uint256 _proposedTimestamp, uint256 _proposalId) external {
       require(s.diamondCutStorage.securityCouncilMembers[msg.sender], "a9"); // not a security council member
       uint256 currentProposalId = s.diamondCutStorage.currentProposalId;
       require(s.diamondCutStorage.securityCouncilMemberLastApprovedProposalId[msg.sender] < currentProposalId, "ao"); // already approved this proposal
@@ -110,6 +110,8 @@ contract DiamondCutFacet is Base, IDiamondCut {

       require(s.diamondCutStorage.proposedDiamondCutTimestamp != 0, "f0