The enforcement of the maximum amount of L2 gas that should be spent on a transaction is flawed as it allows the gas spent to be more than the `Operator's trust limit`
Audit: 2024-03-zksync-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2024-03-zksync/blob/4f0ba34f34a864c354c7e8c47643ed8f4a250e13/code/system-contracts/bootloader/bootloader.yul#L919-L1052


# Vulnerability details


## Proof of Concept

First take a look at https://github.com/code-423n4/2024-03-zksync/blob/4f0ba34f34a864c354c7e8c47643ed8f4a250e13/code/system-contracts/bootloader/bootloader.yul#L919-L1052

```yul
            function processL1Tx(
                txDataOffset,
                resultPtr,
                transactionIndex,
                gasPerPubdata,
                isPriorityOp
            ) {
                // For L1->L2 transactions we always use the pubdata price provided by the transaction.
                // This is needed to ensure DDoS protection. All the excess expenditure
                // will be refunded to the user.
                // Skipping the first formal 0x20 byte
                let innerTxDataOffset := add(txDataOffset, 32)

                let basePubdataSpent := getPubdataCounter()

                let gasLimitForTx, reservedGas := getGasLimitForTx(
                    innerTxDataOffset,
                    transactionIndex,
                    gasPerPubdata,
                    L1_TX_INTRINSIC_L2_GAS(),
                    L1_TX_INTRINSIC_PUBDATA()
                )

                let gasUsedOnPreparation := 0
                let canonicalL1TxHash := 0

                canonicalL1TxHash, gasUsedOnPreparation := l1TxPreparation(txDataOffset, gasPerPubdata, basePubdataSpent)

                let refundGas := 0
                let success := 0

                // The invariant that the user deposited more than the value needed
                // for the transaction must be enforced on L1, but we double check it here
                let gasLimit := getGasLimit(innerTxDataOffset)

                // Note, that for now the property of block.base <= tx.maxFeePerGas does not work
                // for L1->L2 transactions. For now, these transactions are processed with the same gasPrice
                // they were provided on L1. In the future, we may apply a new logic for it.
                let gasPrice := getMaxFeePerGas(innerTxDataOffset)
                let txInternalCost := safeMul(gasPrice, gasLimit, "poa")
                let value := getValue(innerTxDataOffset)
                if lt(getReserved0(innerTxDataOffset), safeAdd(value, txInternalCost, "ol")) {
                    assertionError("deposited eth too low")
                }

                // In previous steps, there might have been already some pubdata published (e.g. to mark factory dependencies as published).
                // However, these actions are mandatory and it is assumed that the L1 Mailbox contract ensured that the provided gas is enough to cover for pubdata.

                if gt(gasLimitForTx, gasUsedOnPreparation) {
                    let gasSpentOnExecution := 0
                    let gasForExecution := sub(gasLimitForTx, gasUsedOnPreparation)