xanoutas/solidity-security-findings huggingface dataset:
Disabling Upgradeability through Misconfigured `executeUpgrade`
Audit: 2023-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/libraries/Diamond.sol#L95


# Vulnerability details

## Description

The `executeUpgrade` function, which handles upgrades in the contract through the Diamond Standard (ERC-2535), utilizes intricate data structures (`FacetCut` and `DiamondCutData`) to direct changes. Given the complexities and the potential to mistakenly alter the `selectorToFacet` mapping, there exists a significant risk:

**Loss of Upgradeability**: If an upgrade operation mistakenly removes the `executeUpgrade` function from the `selectorToFacet` mapping, the diamond would lose its upgradeability. This would result in the contract being permanently locked in its current state, preventing any future modifications or rectifications of existing issues.

Other concerns include:

1. **Facet Action Complexity**: Each `FacetCut` can determine an action on a facet. A mistaken action can interrupt the contract's functionality.
  
2. **Post-upgrade Initialization Risks**: With `initAddress` and `initCalldata` in the `DiamondCutData`, a post-upgrade arbitrary function call is made. A misconfigured initialization can induce vulnerabilities.
  
3. **Bulk Changes, Bulk Risks**: A single flawed `FacetCut` entry in `DiamondCutData` can jeopardize the entire upgrade.
  

## Additional Context

The Diamond Standard (ERC-2535) stipulates a method named `diamondCut` for adding, replacing, or removing functions, which is essential for the upgradeability feature. This method takes an array of `FacetCut` structs, each containing a facet address and an array of function selectors to be updated based on the action specified (Add, Replace, Remove). All changes are executed within a single transaction to prevent data corruption during upgrades.

Immutable functions, defined directly in a diamond or inherited by a diamond proxy contract, are crucial as they cannot be replaced or removed. Any attempt to do so will cause a revert. This highlights the importance of the `executeUpgrade` function and the risks associated with its misconfiguration.

It's also noteworthy that the `diamondCut` method includes parameters for an initialization address and a calldata argument representing a function signature to call upon completion of the upgrade, facilitating automatic facet initialization.

It's important to note that the current facet containing the `executeUpgrade` function is the `DiamondCutFecet`, which is planned to be removed in the next upgrade. If this facet is removed without adding the `executeUpgrade` function to a new `Admin` facet, the contract's upgradability will be lost permanently. Furthermore, the `executeUpgrade` functions in both facets have different selectors, making it impossible to perform a simple replacement.

- `DiamondCutFecet.executeUpgrade`:
  
```solidity
  function executeUpgrade(Diamond.DiamondCutData calldata _diamondCut, bytes32 _p
System Contracts can be called by EOA
Audit: 2023-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/libraries/EfficientCall.sol#L124


# Vulnerability details

## Impact
Any user can call the system contracts assuming they are payable.
This is not high risk since the system contracts can't be called for profit, but could be a problem later.

## Proof of Concept
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/libraries/EfficientCall.sol#L124

Consider the above rawCall, if a user could access this with a non-zero value then it will be passed to the message value simulator contract. This is a system contract, so a user now has the system contract flag on their transaction, this transaction could then be pointed to any system contract.

So how does the user enter the rawCall? One possible entrance is here:
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/DefaultAccount.sol#L133

The external executeTransaction() leads to an unprotected rawCall, which gives access to the system contracts.

## Recommended Mitigation Steps
Drop the isSystemCall flag from the rawCall library.


## Assessed type

Other
The "precompileCall" function has a limitation on the gas limit.
Audit: 2023-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync//blob/main/contracts/libraries/SystemContractHelper.sol#L147


# Vulnerability details

## Impact
An attacker with malicious intent can take advantage of this vulnerability by initiating an external precompiled contract with an excessively high gas limit. This could result in the contract running out of gas and, as a consequence, triggering a revert in its execution. Such an attack has the potential to lead to financial losses within the contract or result in unanticipated alterations to its state, effectively enabling a denial-of-service attack.

## Proof of Concept
The "precompileCall" function in the SystemContractHelper library has a vulnerability that can lead to a contract reversion. This vulnerability occurs because the "precompileCall" function does not have a mechanism to impose a gas limit when it calls external precompiled contracts.

```
   /// @notice Call precompile with given parameters.
    /// @param _rawParams The packed precompile params. They can be retrieved by
    /// the `packPrecompileParams` method.
    /// @param _gasToBurn The number of gas to burn during this call.
    /// @return success Whether the call was successful.
    /// @dev The list of currently available precompiles sha256, keccak256, ecrecover.
    /// NOTE: The precompile type depends on `this` which calls precompile, which means that only
    /// system contracts corresponding to the list of precompiles above can do `precompileCall`.
    /// @dev If used not in the `sha256`, `keccak256` or `ecrecover` contracts, it will just burn the gas provided.
    function precompileCall(uint256 _rawParams, uint32 _gasToBurn) internal view returns (bool success) {
        address callAddr = PRECOMPILE_CALL_ADDRESS;

        // After `precompileCall` gas will be burned down to 0 if there are not enough of them,
        // thats why it should be checked before the call.
        require(gasleft() >= _gasToBurn);
        uint256 cleanupMask = UINT32_MASK;
        assembly {
            // Clearing input params as they are not cleaned by Solidity by default
            _gasToBurn := and(_gasToBurn, cleanupMask)
            success := staticcall(_rawParams, callAddr, _gasToBurn, 0xFFFF, 0, 0)
        }
    }
```


## Tools Used
Manual / VsCode

## Recommended Mitigation Steps
To address this issue, it is advisable to introduce a gas limit enforcement mechanism when invoking external precompiled contracts within the "precompileCall" function. The recommended gas limit for such external precompiled contracts is typically set at 2,000,000 gas, which is the maximum allowable gas consumption for a single transaction. 

```
function precompileCall(uint256 _rawParams, uint32 _gasToBurn) internal view returns (bool success) {
    address callAddr = PRECOMPILE_CALL_ADDRESS;

    require(gasleft() >= _gasToBurn, "Not enough gas to call precompiled contract");
    gasleft() -= _gasToBurn;
    
    uint256 cleanupMask = UINT
Missing Input Validation in SystemContext contract
Audit: 2023-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync//blob/main/contracts/SystemContext.sol#L60
https://github.com/code-423n4/2023-03-zksync//blob/main/contracts/SystemContext.sol#L66


# Vulnerability details

## Impact
Lack of input validation is the process of checking input data to ensure that it is valid and safe to process. Therefore, it is important for contracts to consider the lack of input validation to ensure that what is produced is secure and protected from potential security vulnerabilities. This can be achieved by ensuring that every user input is thoroughly checked and validated at every level of data processing, from user interface to database. By doing so, the application can be reliable and protected from security threats that may arise due to lack of input validation.

## Proof of Concept
The problem arises in the "SystemContext" contract at two specific lines - Line 60 and Line 66. These lines likely involve functions for setting the transaction origin and gas price. However, there is a lack of input validation. In other words, the code does not check whether the provided inputs are valid or safe for use in these functions.

```
  /// @notice Set the current tx origin.
    /// @param _gasPrice The new tx gasPrice.
    function setGasPrice(uint256 _gasPrice) external onlyBootloader {
        gasPrice = _gasPrice;
    }

```

```
    /// @notice Set the current tx origin.
    /// @param _newOrigin The new tx origin.
    function setTxOrigin(address _newOrigin) external onlyBootloader {
        origin = _newOrigin;
    }

```

## Tools Used
Manual / VsCode

## Recommended Mitigation Steps
To resolve this problem, the contract should incorporate input validation mechanisms when configuring the transaction origin and gas price. Specifically, functions like "setTxOrigin" and "setGasPrice" should include checks to ensure that the provided input is not an empty or null address and falls within a sensible and acceptable range.


## Assessed type

Invalid Validation
Force Deployment can lock funds
Audit: 2023-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/ContractDeployer.sol#L254


# Vulnerability details

## Impact
Funds can be stuck in the deployer in case construction of the deployed contract reverts.


## Proof of Concept
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/ContractDeployer.sol#L254

Suppose we call deploy on address with some ETH value. 

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/system-contracts/contracts/ContractDeployer.sol#L344

A call is then performed on the to be constructed contract. If for some reason it reverts, the function still continues through the deploy loop trapping funds in the deployer contract.

## Recommended Mitigation Steps
Add an ownership sweep that lets you rescue trapped funds. Alternatively, before transferring funds, put the mimicCall in a try / catch block.


## Assessed type

Other
L2WethBridge.sol uses a different version of solidity
Audit: 2023-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/zksync/contracts/bridge/L2WethBridge.sol#L1-L3


# Vulnerability details

## Impact
Different Solidity compiler versions are used

## Proof of Concept
    // SPDX-License-Identifier: MIT

    pragma solidity ^0.8.0;

## Recommended Mitigation Steps

Versions must be consistent with each other


## Assessed type

Other
Hardcoding of `l2ShardId` causes origin's mis-identification 
Audit: 2023-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/72f5f16ed4ba94c7689fe38fcb0b7d27d2a3f135/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L82-L109


# Vulnerability details


## Impact

Hardcoding the `l2ShardId` to 0 restricts the system's ability to recognize different message origins, since `0 - shard` & `1 - rollUp` potentially leading to transactional discrepancies or failures, since even if as stated in the docs more `l2ShardIds` could get added in the future which exarcebates the issue.

## Proof of Concept

In the system, the value of `l2ShardId` is hardcoded to 0 in various places, specifically within the [Mailbox.sol contract](https://github.com/code-423n4/2023-10-zksync/blob/72f5f16ed4ba94c7689fe38fcb0b7d27d2a3f135/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L97-L101). This design choice means the system always assumes messages originate from the shard.

```solidity
       // - isService = true (just a conventional value)
        // - l2ShardId = 0 (means that L1 -> L2 transaction was processed in a rollup shard, other shards are not available yet anyway)
        // - txNumberInBatch = number of transaction in the batch
        L2Log memory l2Log = L2Log({
            l2ShardId: 0,
```

Issue is that the above claim seems to be wrong since the [Storage.sol contract](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/Storage.sol#L38) indicates the existence of other shard IDs, notably the porter (1). This suggests a clear potential for mismanagement of messages, especially if the system evolves to include other rollups in the future.

```solidity

/// @dev The log passed from L2
/// @param l2ShardId The shard identifier, 0 - rollup, 1 - porter. All other values are not used but are reserved for

```

As a concrete example, take a look at the [`proveL1ToL2TransactionStatus()`](https://github.com/code-423n4/2023-10-zksync/blob/72f5f16ed4ba94c7689fe38fcb0b7d27d2a3f135/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L82-L109) function

```solidity
    function proveL1ToL2TransactionStatus(
        bytes32 _l2TxHash,
        uint256 _l2BatchNumber,
        uint256 _l2MessageIndex,
        uint16 _l2TxNumberInBatch,
        bytes32[] calldata _merkleProof,
        TxStatus _status
    ) public view override returns (bool) {
        // Bootloader sends an L2 -> L1 log only after processing the L1 -> L2 transaction.
        // Thus, we can verify that the L1 -> L2 transaction was included in the L2 batch with specified status.
        //
        // The semantics of such L2 -> L1 log is always:
        // - sender = L2_BOOTLOADER_ADDRESS
        // - key = hash(L1ToL2Transaction)
        // - value = status of the processing transaction (1 - success & 0 - fail)
        // - isService = true (just a conventional value)
        // - l2ShardId = 0 (means that L1 -> L2 transaction was processed in a rollup shard,
Potential reentrancy vulnerability in L2EthToken contract
Audit: 2023-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync//blob/main/contracts/L2EthToken.sol#L80


# Vulnerability details

## Impact
This vulnerability could be exploited by an attacker to execute numerous withdrawal transactions, which might result in depleting the contract's balance or triggering unanticipated actions. As a consequence, this could lead to financial losses for both the contract itself and its users.

## Proof of Concept
In the "withdraw()" function of the L2EthToken contract, there is a potential security vulnerability related to reentrancy. This vulnerability could enable an attacker to re-enter the function before the ongoing transaction finishes, which may lead to unforeseen actions and potential financial losses.

```
/// @notice Initiate the ETH withdrawal, funds will be available to claim on L1 `finalizeEthWithdrawal` method.
    /// @param _l1Receiver The address on L1 to receive the funds.
    function withdraw(address _l1Receiver) external payable override {
        uint256 amount = msg.value;

        // Silent burning of the ether
        unchecked {
            balance[address(this)] -= amount;
            totalSupply -= amount;
        }

        // Send the L2 log, a user could use it as proof of the withdrawal
        bytes memory message = _getL1WithdrawMessage(_l1Receiver, amount);
        L1_MESSENGER_CONTRACT.sendToL1(message);

        emit Withdrawal(msg.sender, _l1Receiver, amount);
    }
```

## Tools Used
Manual / VsCode

## Recommended Mitigation Steps
To address this vulnerability, the contract should implement the check-effects-interaction pattern, which ensures that all changes to the contract's state occur before any external calls are made. To achieve this, the call to `L1_MESSENGER_CONTRACT.sendToL1()` should be placed at the end of the function, after all state modifications have been completed. Furthermore, it's advisable to incorporate a Reentrancy Guard for this function to prevent reentrant calls. This way, the contract can safeguard against potential reentrancy attacks and maintain the integrity of its operations.

Example:
```
function withdraw(address _l1Receiver) external payable override {
    uint256 amount = msg.value;
 
    // Silent burning of the ether
    unchecked {
        balance[address(this)] -= amount;
        totalSupply -= amount;
    }
 
    emit Withdrawal(msg.sender, _l1Receiver, amount);
 
    // Move external call to the end of the function
    bytes memory message = _getL1WithdrawMessage(_l1Receiver, amount);
    L1_MESSENGER_CONTRACT.sendToL1(message);
}
```


## Assessed type

Reentrancy
L1 transaction requested via mailbox gas limit and gas fee is not properly validated
Audit: 2023-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L365
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/libraries/TransactionValidator.sol#L18
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/libraries/TransactionValidator.sol#L115
https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/libraries/TransactionValidator.sol#L133


# Vulnerability details

## Impact

L1 transaction requested via mailbox gas limit is not properly validated

## Proof of Concept

When requesting transaction via mailbox, the code tries to [validate the gas limit setting](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/facets/Mailbox.sol#L365)

```solidity
   L2CanonicalTransaction memory transaction = _serializeL2Transaction(_priorityOpParams, _calldata, _factoryDeps);

	bytes memory transactionEncoding = abi.encode(transaction);

	TransactionValidator.validateL1ToL2Transaction(transaction, transactionEncoding, s.priorityTxMaxGasLimit);
```

this is calling [TransactionValidator.validateL1ToL2Transaction](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/libraries/TransactionValidator.sol#L18)

the code calls

```solidity
        uint256 l2GasForTxBody = getTransactionBodyGasLimit(
            _transaction.gasLimit,
            _transaction.gasPerPubdataByteLimit,
            _encoded.length
        );

        // Ensuring that the transaction is provable
        require(l2GasForTxBody <= _priorityTxMaxGasLimit, "ui");
```

the code tries to calculate the [l2GasForTxBody](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/libraries/TransactionValidator.sol#L115)

```solidity
uint256 overhead = getOverheadForTransaction(_totalGasLimit, _gasPricePerPubdata, _encodingLength);
```

the code use the formula:

body gas limit = total gas limit - overhead

the code that calculate the overhead is [here](https://github.com/code-423n4/2023-10-zksync/blob/1fb4649b612fac7b4ee613df6f6b7d921ddd6b0d/code/contracts/ethereum/contracts/zksync/libraries/TransactionValidator.sol#L133)

to summarize the logic

- the code compute the batch over head  (step 1)
- the code compute the tx slot over head, overhead from taking up the transaction's slot (step 2)
- the code pick the max value between two overhead above (step 3)
- the code compute the bootloader memory can be derived from encoded_len (step 4)
- the codke pick the max value between step 3 and step 4 (this is step 5)
- the code compute the co
EfficientCall#rawCall function always call _loadFarCallABIIntoActivePtr with _isSystem equal true
Audit: 2023-10-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-10-zksync/blob/main/code/system-contracts/contracts/libraries/EfficientCall.sol#L139


# Vulnerability details

In the `EfficientCall#rawCall` function, there is an inconsistency with the use of the `_isSystem` parameter in the `else` branch. It appears that the `_loadFarCallABIIntoActivePtr` function is called with the value true for the `_isSystem` parameter, regardless of the value of the `_isSystem` parameter passed to the `rawCall` function:
```solidity
File: EfficientCall.sol
117:     /// @notice Perform a `call` without copying calldata to memory.
118:     /// @param _gas The gas to use for the call.
119:     /// @param _address The address to call.
120:     /// @param _value The `msg.value` to send.
121:     /// @param _data The calldata to use for the call.
122:     /// @param _isSystem Whether the call should contain the `isSystem` flag.
123:     /// @return success whether the call was successful.
124:     function rawCall(
125:         uint256 _gas,
126:         address _address,
127:         uint256 _value,
128:         bytes calldata _data,
129:         bool _isSystem
130:     ) internal returns (bool success) {
131:         if (_value == 0) {
132:             _loadFarCallABIIntoActivePtr(_gas, _data, false, _isSystem);
133: 
134:             address callAddr = RAW_FAR_CALL_BY_REF_CALL_ADDRESS;
135:             assembly {
136:                 success := call(_address, callAddr, 0, 0, 0xFFFF, 0, 0)
137:             }
138:         } else {
139:             _loadFarCallABIIntoActivePtr(_gas, _data, false, true);
140: 
141:             // If there is provided `msg.value` call the `MsgValueSimulator` to forward ether.
142:             address msgValueSimulator = MSG_VALUE_SYSTEM_CONTRACT;
143:             address callAddr = SYSTEM_CALL_BY_REF_CALL_ADDRESS;
144:             // We need to supply the mask to the MsgValueSimulator to denote
145:             // that the call should be a system one.
146:             uint256 forwardMask = _isSystem ? MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT : 0;
147: 
148:             assembly {
149:                 success := call(msgValueSimulator, callAddr, _value, _address, 0xFFFF, forwardMask, 0)
150:             }
151:         }
152:     }
```

## Recommended Mitigation Steps

Consider updating L139 in `EfficientCall.sol` to:
```solidity
    _loadFarCallABIIntoActivePtr(_gas, _data, false, _isSystem);
```


## Assessed type

Other