xanoutas/solidity-security-findings huggingface dataset:
withdraw() might underflow
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
``withdraw()`` might underflow, as a result, it will withdraw exceeding the balance and leading to wrong accounting in the balance account. 

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

``withdraw()`` initiates the ETH withdrawal, funds will be available to claim on L1 `finalizeEthWithdrawal` method.

[https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L2EthToken.sol#L80-L94](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L2EthToken.sol#L80-L94)

However, in the following block, it used the "unchecked":

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

As a result, underflow is possible and won't be reverted. For example, when ``balances[address(this)] = 0`` and ``amount = 1``, the new balances[address(this)] = uint256.max. 


## Tools Used
VSCode

## Recommended Mitigation Steps
Do not use the "unchecked" so that the function will revert when underflow occurs. 
```diff
 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);
    }
```
extractNumberFromMeta() extracts the wrong part of the number from Meta
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/SystemContractHelper.sol#L210-L215


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
``extractNumberFromMeta()`` extracts the wrong part of the number from Meta

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

``extractNumberFromMeta()`` is supposed to return the bits [offset..offset+size-1] of the meta.

[https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/SystemContractHelper.sol#L210-L215](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/SystemContractHelper.sol#L210-L215)

However, it extracts the wrong part. The reason why it is wrong is because: the following line will shift ``meta`` to the left ``(256-size-offset)`` bits, actually we need to shift ``meta`` to the left ``offset`` bits to locate the ``offset`` bit, so that the result will have the highest bit for the original bit at "offset". In other words, the number of bits we need to shift to the left in this step has nothing to do with ``size``. The ``size`` only needs to be used in the second step to shift it to the right. 

```javascript
 uint256 shifted = (meta << (256 - size - offset));
```

Due to this error, ``extractNumberFromMeta()`` returns the wrong part from meta.

## Tools Used
VSCode

## Recommended Mitigation Steps
In the following, we assume ``offset`` is relative to the highest bit, if the design uses ``offset`` is relative to the lowest bit, then some minor revision is needed. However, the NatSpec says that the function "return the bits [offset..offset+size-1] of the meta.", so the interpretation that ``offset`` is relative to the highest bit is more reasonable.

```diff
function extractNumberFromMeta(uint256 meta, uint256 offset, uint256 size) internal pure returns (uint256 result) {
        // Firstly, we delete all the bits after the field
-        uint256 shifted = (meta << (256 - size - offset));
+        uint256 shifted = meta << offset;

        // Then we shift everything back
        result = (shifted >> (256 - size));
    }
```
User can withdraw from L2 to L1 with an address(0)
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
If a user initiate an ETH withdrawal with a **_l1Receiver** of **address(0)**, a mistake of his own or from a buggy dapp (which is a realistic scenario as this is a new project and many dapps will be built on it and such error can happen), the transaction will go throught and user will effectively loose the funds being withdrawn, as the bridge will burn them. This would normally not be classified as High, but du to the fact that user fund would be lost permanently, I do believe it does justify it. There would be no way to recover those funds, they would be probably locked forever inside the L1 bridge. _getL1WithdrawMessage.abi.encodePacked will revert in case the _l1Receiver is less then 20 Bytes, but a address(0) will pass. Still, proper input validation should be done at the beginning of the function.

## Proof of Concept
This is the main part of the **L2EthToken.sol** contract. Deploy this in Remix, and do the operations described to reproduce the issue.

```
// SPDX-License-Identifier: MIT

/*
POC - simulate a deposit and widhdraw to address(0)
1) DEPOSIT: depositing ETH in L1 will mint the same amount in L2: mint <valid address A> 1000 wei. 
2) WITHDRAW
    a) Simulate the transferFromTo (called by MsgValueSimulator.sol underthehood) which the compiler will have generated for withdraw function since using msg.value : transferFromTo(<valid address A>, <this>, 500 wei)
    b) For debug purpose, check the balances for <valid address A> and <this>, both should be at 500 wei : balanceOf(<valid address A>), balanceOf(<this>)
    c) Proceed with the withdraw with address(0) : withdraw(<0x0000000000000000000000000000000000000000>)
3) Check the balances of both account. <this> will be 0, and <valid address A> left with only 500 wei, as expected.
*/

pragma solidity >=0.8.0;

interface IMailbox {
    function finalizeEthWithdrawal(
        uint256 _l2BlockNumber,
        uint256 _l2MessageIndex,
        uint16 _l2TxNumberInBlock,
        bytes calldata _message,
        bytes32[] calldata _merkleProof
    ) external;
}

contract Test {
    mapping(address => uint256) balance;
    uint256 public totalSupply;

    function transferFromTo(address _from, address _to, uint256 _amount) external {
        uint256 fromBalance = balance[_from];
        require(fromBalance >= _amount, "Transfer amount exceeds balance");
        unchecked {
            balance[_from] = fromBalance - _amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            balance[_to] += _amount;
        }

        //emit Transfer(_from, _to, _amount);
    }

    function balanceOf(uint256 _account) external view returns (uint256) {
        return balance[address(uint160(_account))];
    }

    function mint(address _account, uint256 _
Potential integer underflow that can cause assets loss to users 
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Attacker can by abusing this can manipulate the total supply (inflation) as well as the balance of l2ethtoken contract 

## Proof of Concept
The withdraw function on l2ethtoken contract does not check whether the msg.sender is having the balance he’s withdrawing or not, furthermore it uncheck decreases it directly, a hacker who withdraw an amount with high eth than the total supply this will makes the total supply underflow, and this will make several issue like inflation, token unbalancing, manipulation of address(this) balance and so on 
 
Used truffle and ganache, deploy l2ethtoken contract without minting
Then with attacker make a withdrawal of 1 eth
´´´
const attacker = choose from ganache;
await l2token.balanceOf(l2token.address)

await l2token.totalsupply()
// both of them will returns ‘0’
// as attacker make a withdrawal of 1 wei
await l2token.withdraw(attacker,{from:attacker,value:1})
// attack will succeed, recall the balance commands you will see the price has been underflown 

await l2token.balanceOf(l2token.address)
await l2token.totalsupply()
// ‘115792089237316195423570985008687907853269984665640564039457584007913129639935‘ 
´´´
## Tools Used
Manual review, truffle and ganache 
## Recommended Mitigation Steps
Add this line in the withdraw function 

´´´

require(balance[msg.sender] >= amount, "Withdraw amount exceeds balance");

´´´
Centralisation risk : system contracts can take all funds/transfer from address(0)
Audit: 2023-03-zksync-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
The system contracts and deployer can take funds from any address even the 0x00 address on the L2EthToken without any time lock or approval by the system/governance, as result this can lead to some risk on the protocol 

## Proof of Concept
See the transferFromTo function on ‘L2EthToken’ :

‘ require(
            msg.sender == MSG_VALUE_SYSTEM_CONTRACT ||
                msg.sender == address(DEPLOYER_SYSTEM_CONTRACT) ||
                msg.sender == BOOTLOADER_FORMAL_ADDRESS,
            "Only system contracts with special access can call this method"
        );’

Once the check is done the transfer will be made without 
* check on address(0) is the from address 
* governance agreement nor allowance approval 

Deploy the L2EthToken contract (Used truffle and ganache )

With bootloader mint userA some funds :

´´´
const userA = choose address from ganache;
const userB = choose address from ganache;
 // make a mint 
await l2token.mint(userA,1000,{from:bootload})
// now userA have funds 
await l2token.balanceOf(userA)
// will returns ‘1000’

await l2token.balanceOf(userB)
// will returns ‘0’

// as deployer you can take control over all his funds 

await l2token.transferFromTo(userA,userB,1000,{from:dep})


await l2token.balanceOf(userA)
// will returns ‘0’

await l2token.balanceOf(userB)
// will returns ‘1000’
// the same when there’s some are burnt via address 0
// the function does not check if the address from is 0
´´´
## Tools Used
Manual 

## Recommended Mitigation Steps
Make a bool to allow this centralisation and make it editable via governance/owners 

´´´
bool transferallowed;
…
modifier checkcentre() {
   if(transferallowed) {_;}
}

// add the modifier 

function transferFromTo(address _from, address _to, uint256 _amount) external checkcentre 
…
´´´
Here are some potential security vulnerabilities that I have identified in this particular contract (BytecodeCompressor.sol)
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L43
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L62-L63
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L50


# Vulnerability details

## Impact

1. Integer overflow: In the `publishCompressedBytecode` function, the check `dictionary.length <= 2 ** 16 * 8` is intended to ensure that the dictionary does not become too large, but it is not sufficient to prevent an integer overflow. If `dictionary.length` exceeds `2 ** 16`, the multiplication `dictionary.length * 8` will overflow, and the check will pass even if the dictionary is too large. This can be fixed by changing the check to `dictionary.length <= 2 ** 13`.

2. Out-of-bounds access: In the publishCompressedBytecode function, the index indexOfEncodedChunk is calculated as `uint256(encodedData.readUint16(encodedDataPointer)) * 8`, which is then used to read a 64-bit value from the dictionary array. If indexOfEncodedChunk exceeds `dictionary.length`, an out-of-bounds access will occur and may result in unexpected behavior or a crash. This can be fixed by adding a check to ensure that `indexOfEncodedChunk` is within the bounds of the dictionary array.

3. Reentrancy: The `publishCompressedBytecode` function makes two external calls: one to the `sendToL1` function of an unknown contract and another to the `markBytecodeAsPublished` function of a storage contract. If either of these functions can call back into the `BytecodeCompressor` contract, it could result in a reentrancy vulnerability. This can be mitigated by using the "checks-effects-interactions" pattern and ensuring that any external calls are made at the end of the function after all state changes have been made.

4. Unauthenticated access: The `publishCompressedBytecode` function can be called by anyone, which means that anyone can publish compressed bytecode on behalf of someone else. If the `KNOWN_CODE_STORAGE_CONTRACT` is used to verify the authenticity of bytecode, this could lead to unauthorized code being marked as published. This can be fixed by adding an authentication mechanism to ensure that only authorized users can publish compressed bytecode.

## Proof of Concept
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L35-L68.

```diff
function publishCompressedBytecode(
        bytes calldata _bytecode,
        bytes calldata _rawCompressedData
    ) external payable returns (bytes32 bytecodeHash) {
        unchecked {
            (bytes calldata dictionary, bytes calldata encodedData) = _decodeRawBytecode(_rawCompressedData);

            require(dictionary.length % 8 == 0, "Dictionary length should be a multiple of 8");
            require(dictionary.length <= 2 *
Wrong Implementation of EIP-712
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/TransactionHelper.sol#L81


# Vulnerability details

## Impact
The EIP-712 uses several parameters. 

Those parameters are exactly:

     EIP712Domain {
     string name;
    string version;
    uint256 chainId;
    address verifyingContract;
    }

As you can see on the following Domain, ZkSync, is missing one parameter:

     bytes32 constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string 
      version,uint256 chainId)");

which is the verifyingContract.

missing the contract in the EIP-712, would cause to open an attack vector such has signature replay. As the contract does not hash the address(this) to generate the signature and recover from it, it is causing this potential signature replay across contracts from the same chainId.

That will also brick the _encodeHashEIP712Transaction function because the hash returned will not be the correct one:

     return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));


## Proof of Concept


## Tools Used
Manual

## Recommended Mitigation Steps

Add the address(this) in the TYPEHASH to protect against replay attacks

bytes32 constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string 
      version,uint256 chainId, address contract)");
My Findings
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L43
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L62-L63
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L50


# Vulnerability details

## Impact

1. Integer overflow: In the `publishCompressedBytecode` function, the check `dictionary.length <= 2 ** 16 * 8` is intended to ensure that the dictionary does not become too large, but it is not sufficient to prevent an integer overflow. If `dictionary.length` exceeds `2 ** 16`, the multiplication `dictionary.length * 8` will overflow, and the check will pass even if the dictionary is too large. This can be fixed by changing the check to `dictionary.length <= 2 ** 13`.

2. Out-of-bounds access: In the publishCompressedBytecode function, the index indexOfEncodedChunk is calculated as `uint256(encodedData.readUint16(encodedDataPointer)) * 8`, which is then used to read a 64-bit value from the dictionary array. If indexOfEncodedChunk exceeds `dictionary.length`, an out-of-bounds access will occur and may result in unexpected behavior or a crash. This can be fixed by adding a check to ensure that `indexOfEncodedChunk` is within the bounds of the dictionary array.

3. Reentrancy: The `publishCompressedBytecode` function makes two external calls: one to the `sendToL1` function of an unknown contract and another to the `markBytecodeAsPublished` function of a storage contract. If either of these functions can call back into the `BytecodeCompressor` contract, it could result in a reentrancy vulnerability. This can be mitigated by using the "checks-effects-interactions" pattern and ensuring that any external calls are made at the end of the function after all state changes have been made.

4. Unauthenticated access: The `publishCompressedBytecode` function can be called by anyone, which means that anyone can publish compressed bytecode on behalf of someone else. If the `KNOWN_CODE_STORAGE_CONTRACT` is used to verify the authenticity of bytecode, this could lead to unauthorized code being marked as published. This can be fixed by adding an authentication mechanism to ensure that only authorized users can publish compressed bytecode.

## Proof of Concept
https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L35-L68.

```diff
function publishCompressedBytecode(
        bytes calldata _bytecode,
        bytes calldata _rawCompressedData
    ) external payable returns (bytes32 bytecodeHash) {
        unchecked {
            (bytes calldata dictionary, bytes calldata encodedData) = _decodeRawBytecode(_rawCompressedData);

            require(dictionary.length % 8 == 0, "Dictionary length should be a multiple of 8");
            require(dictionary.length <= 2 *
`L2EthToken.sol` `balance[address(this)]` COULD UNDERFLOW
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
The ethereum balance of the `L2EthToken.sol` contract can underflow thus breaking the accounting of the protocol for L2-L1 fund transfers.

## Proof of Concept

`L2EthToken.sol` contract has the `withdraw` function to transfer funds to L1 for withdrawal.

In the withdraw function the internal accounting is done for the `amount` withdrawn from the contract. The accounting is done inside the `unchecked` block which means overflow and underflow checks will not be performed for the arithmetic operations happening inside the `unchecked` block.

The mentioned internal accounting is done as follows in the code:

        unchecked {
            balance[address(this)] -= amount;
            totalSupply -= amount;
        }
In this case the following scenario can happen:

1 . The contract has relatively less funds stored in it due to previous withdrawals. 
2 . A particular user who is withdrawing larger value (msg.value) from the contract by calling the withdraw function.
3 . Hence `balance[address(this)] < amount` can occur.
4 . No check for `balance[address(this)] > amount` is performed before the internal accounting inside the `unchecked` block.
5 . The `balance[address(this)] -= amount` operation can underflow.
6 . If the above condition underflows the `balance[address(this)]` value will be stored as very large value.
7 . This could further impact the `transferFromTo` function since following `unchecked` block can overflow if the `address(this)` is used as either `_from` or `_to`. Since balance[address(this)] will now be a very large value due to previous underflow.

        unchecked {
            balance[_from] = fromBalance - _amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            balance[_to] += _amount; 
        }

8 . As it is depicted above, this will completely break the entire accounting system of the fund transfers since the contract funds amount is now erroneous.

## Tools Used

VS Code and Manual Review

## Recommended Mitigation Steps

Perform the `balance[address(this)] -= amount` calculation outside the `unchecked` block. So the transaction will revert in the event of an underflow.
Unchecked return values in setValueForNextCall 
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L320


# Vulnerability details

## Title: 
Description: When creating a contract, if there is value to be transferred the `_constructContract` function of `ContractDeployer` will use the `SystemContractsHelper.setValueForNextFarCall` Method: https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/SystemContractHelper.sol#L161-L172 

However in `_constructContract` function doesn't verify that the value returned was success: https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/ContractDeployer.sol#L320

As a result this could lead to an incorrect handling of `msg.value` and balances for contracts. Currently this may not lead to loss of funds since the `L2EthToken` contract is called and the balance is successfully transferred there, but this could have downstream consequences if the created contract uses the `msg.value` variable in some way.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Check the return value and revert the transaction if `success != true`.