xanoutas/solidity-security-findings huggingface dataset:
Division before multiply results in precision loss
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact 
Precision loss occurs when division is carried out before multiplication, that can affect gas calculation in terms of publishing the data to L1.

## Proof of Concept
https://github.com/code-423n4/2023-03-zksync//blob/main/contracts/L1Messenger.sol#L37

## Tools Used
Manual Review

## Recommended Mitigation Steps
Recommendation is made for avoiding divison before multiplication and always performs division in last to maintain precision.
Unsafe system contract verification
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
On the following function: 

    function isSystemContract(address _address) internal pure returns (bool) {
        return uint160(_address) <= uint160(MAX_SYSTEM_CONTRACT_ADDRESS);
    }
     }

it does check whether an address is a system contract by checking whether it is smaller than MAX_SYSTEM_CONTRACT_ADDRESS, which it is defined in the constant file as:

      uint160 constant MAX_SYSTEM_CONTRACT_ADDRESS = 0xffff; // 2^16 - 1

the 0xFFFF can be used to represent the maximum value that can be stored in a 16-bit unsigned integer. Therefore it represents 65,535 in decimal.

This means that any address smaller than: 

        0x00000000000000000000000000000000000065535  //note that it has an invalid checksum, just for explanation 
           purposes

will be treated as a system contract. Including address(0). Even though it is very unlikely there are or can be addresses < MAX_SYSTEM_CONTRACT_ADDRESS  which are not form the system, and because of the validation that currently exists  return uint160(_address) <= uint160(MAX_SYSTEM_CONTRACT_ADDRESS);   they would be treated as system contracts, having permissions to call different permission restricted functions and increase nonces from different accounts:

        modifier onlySystemCall() {
        require(
            SystemContractHelper.isSystemCall() || SystemContractHelper.isSystemContract(msg.sender),
            "This method require system call flag"
        );
        _;
        }

https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/MsgValueSimulator.sol#L32
 fallback(bytes calldata _data) external payable onlySystemCall returns (bytes memory) {


https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/NonceHolder.sol#L134
 function increaseMinNonce(uint256 _value) public onlySystemCall returns (uint256 oldMinNonce) {

## Proof of Concept

PoC to check that the addresses < 65535 return true:

     pragma solidity ^0.8;
     contract ZkSync{

       /// i.e. their addresses must be below 2^16.
       uint160 constant MAX_SYSTEM_CONTRACT_ADDRESS = 0xffff; // 2^16 - 1

        function isSystemContract(address _address) public pure returns (bool) {

        return uint160(_address) <= uint160(MAX_SYSTEM_CONTRACT_ADDRESS);
    }

     }

     contract checkSystem{
    function test()public pure returns (bool succ){
    succ = ZkSync(0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC).isSystemContract(address(0));
    }
     }


## Tools Used
Manual, Remix, VsCode

## Recommended Mitigation Steps
Mantain a whiltelist of system contracts inside a mapping with a bool. Only map the system contract and pair them as true. An check on the modifier whether 

      mapping(address => bool) isSystemContract;
      
     mod
bootloader doesn't add tighter gas limit to the IAccount.validateTransaction call
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/main/bootloader/bootloader.yul#L2165-L2193


# Vulnerability details

## Impact
As mentioned in the competition details: `*Important, while the bootloader is out of scope, we may reward an additional bounty for valid bugs found in it by our judgement!`

As mentioned in the dev document, https://era.zksync.io/docs/dev/developer-guides/aa.html#limitations-of-the-verification-step, there are some limitations of the verification are designed to protect the system from a DoS threat. The verification rules are not fully enforced right now. But at present, the tx verification step and the tx execution step share the same gas limit (max to the MAX_GAS_PER_TRANSACTION config). 

Bootloader doesn't add tighter gas limit to the verification, which is mainly IAccount.validateTransaction call. It will make all other limitation rules meaningless, because the attacker can force the bootloader to execute the garbage codes continuously in the verification process until the MAX_GAS_PER_TRANSACTION gas is used up. This makes it become possible to DOS the operators because any gas cost in the verification will be revert if the verification result is invalid.

## Proof of Concept
The call flow for the transaction verification in the bootloader is:
```
processL2Tx -> l2TxValidation -> ZKSYNC_NEAR_CALL_validateTx -> accountValidateTx -> callAccountMethod -> IAccount.validateTransaction
```
The gas limit passed to the IAccount.validateTransaction call is defined in the processL2Tx function by `getGasLimitForTx`:
```
                let gasLimitForTx := getGasLimitForTx(innerTxDataOffset, transactionIndex, gasPerPubdata, L2_TX_INTRINSIC_GAS(), L2_TX_INTRINSIC_PUBDATA())
```
The main logic is that:
```
let operatorTrustedErgsLimit := max(MAX_GAS_PER_TRANSACTION(), getOperatorTrustedGasLimitForTx(transactionIndex))
totalGasLimit := min(operatorTrustedErgsLimit, totalGasLimit)
```
And the getOperatorTrustedGasLimitForTx is like cal the `estimateGas` rpc call for a tx. So if your tx used for DOS attack is designed as reverting forever which make it return noting as trusted gas limit, or it has to cost huge amounts of gas which make it return a large enough gas limit, the final gas limit will be `MAX_GAS_PER_TRANSACTION() - some intrinsic costs and overhead`. The tx verification and the tx execution step share the same gas limit (max to the MAX_GAS_PER_TRANSACTION config).It's 80000000 in the current SystemConfig.json, which is large enough to DOS the operator.

## Tools Used
Manual review
## Recommended Mitigation Steps
Bootloader should add tighter gas limit to the IAccount.validateTransaction call
NonceHolder.setValueUnderNonce does not increase the minNonce when using sequential account nonce ordering
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/NonceHolder.sol#L81-L96


# Vulnerability details

## Impact
The current available nonce can't be synchronized to the client and rpc resp in real time. As a result, the client can't use the correct nonce to signature transaction.

## Proof of Concept
If the nonceOrdering type of the account is `IContractDeployer.AccountNonceOrdering.Sequential`, the next nonce to use should be the minNonce. But if the `NonceHolder.setValueUnderNonce` function sets a value under this minNonce, the minNonce will be marked as used in the `NonceHolder.isNonceUsed` function :
```
return (_nonce < getMinNonce(_address) || 
        nonceValues[addressAsKey][_nonce] > 0 //  <--- Marked here
);
```
The transaction nonce check logic is in the bootloader.yul, `ensureNonceUsage` function. The `ensureNonceUsage` function will be called before call the `validateTransaction` of the account to ensure the nonce of the transaction is not used. And it will be called again after the `validateTransaction` function to ensure the nonce of the transaction is marked as used, https://github.com/code-423n4/2023-03-zksync/blob/main/bootloader/bootloader.yul#L2174-L2192.  And the `ensureNonceUsage` function will call the `NonceHolder.validateNonceUsage` function to ensure the nonce state. 

But when the `NonceHolder.setValueUnderNonce` function sets a value on the current minNonce, it does not increase the minNonce. This causes the client and rpc can't update the next nonce in real time. As a result, the client can't use the correct nonce to signature next transaction.

## Tools Used
Manual review
## Recommended Mitigation Steps
Increase nonce when setValueUnderNonce if it's using sequential account nonce ordering.
NonceHolder.setValueUnderNonce does not check if the nonce has been used before
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/NonceHolder.sol#L81-L96


# Vulnerability details

## Impact
Already used nonces or the nonces less than the specified minimal nonce can be used by the `NonceHolder.setValueUnderNonce` function. It can lead to some unique conflicts and even replay attacks on some of dapps which are based on the values under nonces.

## Proof of Concept
If the nonceOrdering type of the account is `IContractDeployer.AccountNonceOrdering.Sequential`, the account will only check if the `key - 1` nonce is used:
```
if (accountInfo.nonceOrdering == IContractDeployer.AccountNonceOrdering.Sequential && _key != 0) {
            require(isNonceUsed(msg.sender, _key - 1), "Previous nonce has not been used");
        }
```
But it does not check if the _key is less the minNonce, so setValueUnderNonce can write value on the used nonce. And the function also does not check if the `nonceValues[addressAsKey][_key] != 0`, which make it possiable to overwrite the values under the used nonces.

## Tools Used
Manual review
## Recommended Mitigation Steps
Check if the nonce has been used before and if the nonce is not less than min nonce.
DefaultAccount will add system call flag to any call with msg.value
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/DefaultAccount.sol#L150-L154
https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/libraries/EfficientCall.sol#L134-L145
https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/MsgValueSimulator.sol#L22-L29
https://github.com/code-423n4/2023-03-zksync/blob/main/contracts/MsgValueSimulator.sol#L62


# Vulnerability details

## Impact
As mentioned in the repo's README.md documentation:
```
`isSystem` flag. Whether the call intends a system contracts' function. While most of the system contracts' functions are relatively harmless, accessing some with calldata only may break the invariants of Ethereum
```

The DefaultAccount will add `isSystem` flag explicitly to any transaction(call) if the `msg.value` of the transaction is not zero. These transactions will bypass the `onlySystemCall` check directly. 

`onlySystemCall` modifier in the README.md documentation:
```
That's why we wanted to make it clear that users can not invoke potentially dangerous operations by doing a simple EVM-like call. Whenever a user wants to invoke some of the operations which we considered dangerous, they must explicitly provide isSystem flag with them.
```

## Proof of Concept
In the `DefaultAccount._execute` function, if the `to` address is not DEPLOYER_SYSTEM_CONTRACT address, it will use the function `EfficientCall.rawCall(gas, to, value, data)` to forward the call.

In the `EfficientCall.rawCall` function if the `_value` param, which is from msg.value, is not zero, it will simulate `system_call_byref` opcode to call the `MsgValueSimulator` system contract with the following params: 
```
success := call(msgValueSimulator, callAddr, _value, _address, 0xFFFF, forwardMask, 0)
```
And the forwardMask will be set to `MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT`.

Now the context is coming in the `MsgValueSimulator.fallback` function. It will get the above mask and set it as the `isSystemCall` param:
```
isSystemCall = (mask & MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT) != 0;
```
The `isSystemCall` param will make the final mimicCall forward the call with the `isSystem` flag:
```
EfficientCall.mimicCall(gasleft(), to, _data, msg.sender, false, isSystemCall);
```

## Tools Used
Manual review


## Recommended Mitigation Steps
Should let the account choose whether or not to add the system mask in the `DefaultAccount._execute` function when forwarding the call to the `MsgValueSimulator`.
Users pay excessive gas cost for sending bytecode hashes to L1
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
When deploying new contracts, users are forced to pay more gas than is required to publish the bytecode hash to the L1.
## Proof of Concept
When users deploy new smart contracts, the protocol marks the hashes of the bytecodes of the contracts as known and sends the bytecode to the L1. As per the description of the project:
> On zkSync, the L2 stores the contract's code hashes and not the codes themselves.

And:

> If the transaction comes from L2, i.e. (the factory dependencies are yet to publish on L1), the operator prepares the compress the bytecode offchain and then verifies that the bytecode was compressed correctly. After that, we send the L2→L1 log with the compressed bytecode of the contract. It is the responsibility of the L1 contracts to verify that the corresponding bytecode hash has been published on L1.

There are two functions that handle publishing of bytecodes and their hashes to the L1 network:
1. [BytecodeCompressor.publishCompressedBytecode](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L35) validates compressed bytecode and publishes it to the L1. The functions [calls](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/BytecodeCompressor.sol#L62) the [L1Messenger.sendToL1](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L1Messenger.sol#L22) method to send the bytecode to the L1. Inside `sendToL1`, the amount of the gas that the transaction sender needs to pay is [computed](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L1Messenger.sol#L39) as:
    ```solidity
    uint256 pubdataLen;
    unchecked {
        pubdataLen = ((_message.length + 31) / 32) * 32 + 64;
    }
    uint256 gasToPay = pubdataLen * gasPerPubdataBytes;
    ```
    I.e. the user sends the rate of `gasPerPubdataBytes` per each byte of the sent data (that is, the bytecode).
    Actual publishing of bytecode is implemented by [emitting a message](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L1Messenger.sol#L51):
    ```solidity
    emit L1MessageSent(msg.sender, hash, _message);
    ```
    To put it simply, the user pays for the size of the bytecode.
1. [KnownCodesStorage.markFactoryDeps](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/KnownCodesStorage.sol#L31) is used to publish bytecode hashes to the L1. The bootloader [always calls this function when processes transaction factory dependencies](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/bootloader/bootloader.yul#L1345). `markFactoryDeps` calls `_ma
Default accounts cannot pay transaction fees due to `DefaultAccount` not calling `MsgValueSimulator`
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
Default accounts cannot pay the transaction fees to the bootloader. It's not clear whether the attempts to do so will silently succeed or revert because the behaviour of the `CALL` opcode in the zkSync Era virtual machine isn't explained in the description of the project.
## Proof of Concept
As per the description of the project:
> Unlike Ethereum, zkEVM does not have any notion of any special native token. That's why we have to simulate operations with Ether via two contracts: L2EthToken & MsgValueSimulator.

Also:
> L2EthToken is a contract that holds the balances of ETH for the users. [...] The only method for transferring Ether is transferFromTo. It permits only some system contracts to transfer on behalf of users. This is needed to ensure that the interface is as close to Ethereum as possible, i.e. the only way to transfer ETH is by doing a call to a contract with some msg.value. This is what MsgValueSimulator system contract is for.

[DefaultAccount](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/DefaultAccount.sol#L18) is the default implementation of the Account Abstraction protocol: all EOAs on zkSync Era will use it as their default account implementation. The contract implements [payForTransaction](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/DefaultAccount.sol#L199) intended to pay transaction fees on behalf of the user. The method calls [TransactionHelper.payToTheBootloader](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/TransactionHelper.sol#L394) to pay a transaction fee, however `payToTheBootloader` uses the `CALL` opcode to make the payment. As per the description cited above, this should not work because the only way to transfer ETH on zkSync Era is by calling [L2EthToken.transferFromTo](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/L2EthToken.sol#L40) via [MsgValueSimulator](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/MsgValueSimulator.sol#L37).

Unfortunately, the description of the project doesn't explain how transferring `msg.value` via the low-level `CALL` opcode works on the virtual machine of zkSync Era. But, according to the description of the `L2EthToken` and `MsgValueSimulator` contracts, it shouldn't work at all.

It's also likely that this was thoroughly tested since paying transaction fees is a crucial feature of the AA protocol. And it could be so that the testing environment didn't fully replicate the behaviour of the virtual machine, letting transferring of ETH via the `CALL` opcode pass.
## Tools Used
Manual preview
## Recommended Mitigation S
User transactions can call system contracts directly
Audit: 2023-03-zksync-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
User transaction can call system contracts directly, which shouldn't be allowed to not invoke potentially dangerous operations.
## Proof of Concept
The [DefaultAccount.executeTransaction](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/DefaultAccount.sol#L117) executes a user transaction after it was validated. The function calls [_execute](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/DefaultAccount.sol#L140) under the hood. The `_execute` function makes two different calls depending on the destination address of a transaction:
1. if the `ContractDeployer` is called, it'll [pass the call to the contract](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/DefaultAccount.sol#L149) via the system call (`ContractDeployer` is a system contract and can be executed only via system calls);
1. if any other contract is called, it'll [execute the call via `EfficientCall.rawCall`](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/DefaultAccount.sol#L151).

`EfficientCall.rawCall` in its turn also makes two different calls:
1. If `msg.value` of the transaction is 0, it'll [make a regular call](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/EfficientCall.sol#L131).
1. If there's some ETH sent with the transaction (i.e. `msg.value` is positive), it'll [pass the call to the `MsgValueSimulator` contract](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/EfficientCall.sol#L144). `MsgValueSimulator` is a system contract, thus the `isSystem` flag will be set in the far call ABI (notice the `true` in the [last argument of `_loadFarCallABIIntoActivePtr`](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/EfficientCall.sol#L134)). However, it'll also [set the forward mask](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/libraries/EfficientCall.sol#L141) to 1 (the value of [MSG_VALUE_SIMULATOR_IS_SYSTEM_BIT](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/Constants.sol#L71)). `MsgValueSimulator` will [extract the mask and will set the `isSystemCall` flag to true](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/MsgValueSimulator.sol#L27)–it'll then pass the `isSystemCall` flag to the [subsequent call](https://github.com/code-423n4/2023-03-zksync/blob/21d9a364a4a75adfa6f1e038232d8c0f39858a64/contracts/MsgValueSimulator.sol#L62), making the