xanoutas/solidity-security-findings huggingface dataset:
In L1KakarotMessaging, address alias is applied to all addresses leading to DoS in Kakarot EVM. 
Audit: 2024-09-kakarot-findings
Severity: MEDIUM

# Lines of code

https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/solidity_contracts/src/L1L2Messaging/L1KakarotMessaging.sol#L35


# Vulnerability details

## Description
In the `L1KakarotMessaging` contract, all messages that are sent to the L2 will include a sender address. Due to [the security concerns](https://docs.optimism.io/chain/differences#address-aliasing), an address aliasing is applied to the sender addresses. This can be seen in the `sendMessageToL2` [function](https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/solidity_contracts/src/L1L2Messaging/L1KakarotMessaging.sol#L35):

```Solidity
 function sendMessageToL2(address to, uint248 value, bytes calldata data) external payable {
        uint256 totalLength = data.length + 4;
        uint256[] memory convertedData = new uint256[](totalLength);
        convertedData[0] = uint256(uint160(AddressAliasHelper.applyL1ToL2Alias(msg.sender)));
```

The `applyL1ToL2Alias` function basically adds a certain offset to the sender's address. If the receiving contract wants to get back the original address this address can be un-aliased by subtracting the offset. This can be seen in the `AddressAliasHelper` [contract](https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/solidity_contracts/src/L1L2Messaging/AddressAliasHelper.sol#L30):

```Solidity
    function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) {
        unchecked {
            l2Address = address(uint160(l1Address) + offset);
        }
    }
```

The problem is this aliased address is directly used by the Kakarot as the sender's address in the Kakarot. In the L1 -> L2 messages, `l1_sender` is an [aliased address](https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/kakarot/kakarot.cairo#L380):
```
let (_, state, _, _) = Kakarot.handle_l1_message(l1_sender, to_address, value, data_len, data);
```

This means that if a user wants to send an L1 -> L2 message to Kakarot, they will never be able to use their own address. The sender balance will always be zero because the aliased address will not have any balance. If the L1 -> L2 message contains a value, this will be deducted from the L1 aliased address on L2. However, this aliased address is not an actual account on L2. It will never actually hold a balance on L2 unless the user specifically sends native tokens to the aliased address on L2. For this reason, any transaction that sends value will revert on Starknet.    



## Recommended Mitigation Steps

Only alias addresses that originate from smart contracts. In fact that's how it's done in [the Optimism as well](https://docs.optimism.io/chain/differences#address-aliasing):
```
When transactions are sent from L1 to L2 by an Externally Owned Account, the address of the sender of the transaction on L2 will be set to the address of the sender of the transaction on L1. However, the a
Lack of verification for Invalid Points (Subgroup Checks) for coordinates can brick elliptic curve operations
Audit: 2024-09-kakarot-findings
Severity: MEDIUM

# Lines of code

https://github.com/kkrt-labs/kakarot-ssj/blob/d4a7873d6f071813165ca7c7adb2f029287d14ca/crates/evm/src/precompiles/ec_operations/ec_add.cairo#L78
https://github.com/kkrt-labs/kakarot-ssj/blob/d4a7873d6f071813165ca7c7adb2f029287d14ca/crates/evm/src/precompiles/ec_operations/ec_add.cairo#L90
https://github.com/kkrt-labs/kakarot-ssj/blob/d4a7873d6f071813165ca7c7adb2f029287d14ca/crates/evm/src/precompiles/ec_operations/ec_add.cairo#L104
https://github.com/kkrt-labs/kakarot-ssj/blob/d4a7873d6f071813165ca7c7adb2f029287d14ca/crates/evm/src/precompiles/ec_operations/ec_mul.cairo#L66
https://github.com/kkrt-labs/kakarot-ssj/blob/d4a7873d6f071813165ca7c7adb2f029287d14ca/crates/evm/src/precompiles/ec_operations.cairo#L22


# Vulnerability details

## Summary

The **Kakarot-ssj** precompiled files perform elliptic curve operations for  **BN254**, using the `is_on_the_curve` function to validate points. However, this function lacks an important check for small subgroups. To enhance security, it is essential to verify that points belong to the main subgroup by ensuring that multiplying a point by the curve's order results in the point at infinity. This validation is vital for cryptographic integrity.


## Vulnerability Details

In **Kakarot-ssj**, the precompiled files handle elliptic curve cryptography operations, particularly for the **BN254**. These operations include `ec_mul` and `ec_add`, which are utilized for elliptic curve multiplication and addition, respectively. Both operations validate whether the supplied parameters (i.e., points on the curve) are indeed on the curve by invoking the function `is_on_the_curve()`. This function returns a boolean value indicating the validity of the given point on the elliptic curve.

The `is_on_the_curve()` function essentially performs two checks:

- It verifies that the provided point lies on the BN254 curve.
- It ensures that the x and y coordinates of the point do not correspond to the point at infinity (0,0).

Also, within the `ec_mul()` and `ec_add()` functions, there's another important check for elliptic curves ensuring that any point with an x or y coordinate greater than the modulus is considered invalid:

```
if x1 >= BN254_PRIME || y1 >= BN254_PRIME {
        return Option::None;
    }

```

However, one important validation is missing to confirm that the point is a valid point on the curve: the check for **small subgroups**. This check involves verifying that any point (or multiple points) used in cryptographic operations belongs to the main subgroup defined by the elliptic curve's order.

It is true that BN254 is a pairing-friendly curve because it has a cofactor of 1 for its prime-order subgroup, but this does not automatically guarantee that subgroup checks can be ignored in all scenarios.

For example, in certain situations where inputs are received from untrusted parties, subgroup checks still offer additional safety since a malicious party supplies points that do not belong to the
Missing Check for Scalar Validity in ec_mul.cairo
Audit: 2024-09-kakarot-findings
Severity: MEDIUM

# Lines of code

https://github.com/kkrt-labs/kakarot-ssj/blob/d4a7873d6f071813165ca7c7adb2f029287d14ca/crates/evm/src/precompiles/ec_operations/ec_mul.cairo#L54


# Vulnerability details

## Summary

In the provided `ec_mul` function, there is no check to ensure that the scalar `s` is valid within the context of elliptic curve operations. The function only checks whether `s = 0` or `s = 1`, but no check is done to ensure `s` is smaller than the group order of the BN254 curve.

## Vulnerability details

Exceeding the group order in elliptic curve cryptography occurs when a scalar s used in scalar multiplication is greater than or equal to the curve’s group order. 

The group order n is the number of points in the elliptic curve’s main subgroup, which includes all the points used in secure cryptographic operations. The group is cyclic, meaning that after multiplying any point P by the group order n, the result is the point at infinity.

What Happens If the Scalar Exceeds the Group Order?

If the scalar s is larger than the group order, the multiplication will essentially "wrap around." This is because as we implied, the elliptic curve behaves in a cyclic way. It means that the result of multiplying a point by s is the same as multiplying it by s mod n (i.e., the remainder when s is divided by n).

The following PoC will demonstrante the issue:

Using the `test_ec_mul` we can put the value of s as the group order which is defined by 
`const BN254_ORDER: u256 = 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001` or essentially
21888242871839275222246405745257275088548364400416034343698204186575808495617:


```rust
    #[test]
    fn test_ec_mul() {
        let (x1, y1, s) = (1, 2, 21888242871839275222246405745257275088548364400416034343698204186575808495617);
        let (x, y) = ec_mul(x1, y1, s).expect('ec_mul failed');
        println!("Value of x is {}: ", x);
        println!("Value of y is {}: ", y);     
    }

```

The output are the points at infinity:

```rust
Value of x is 0: 
Value of y is 0: 
[PASS] evm::precompiles::ec_operations::ec_mul::tests::test_ec_mul (gas: ~1164)
Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 398 filtered out

```

If we put the value of s greater than the group order just by 1 (meaning 21888242871839275222246405745257275088548364400416034343698204186575808495618) we simply wrap around the multiplication, as described above

```
    #[test]
    fn test_ec_mul() {
        let (x1, y1, s) = (1, 2, 21888242871839275222246405745257275088548364400416034343698204186575808495618);
        let (x, y) = ec_mul(x1, y1, s).expect('ec_mul failed');
        println!("Value of x is {}: ", x);
        println!("Value of y is {}: ", y);
     
    }
```
Console output:

```
Value of x is 1: 
Value of y is 2: 
[PASS] evm::precompiles::ec_operations::ec_mul::tests::test_ec_mul (gas: ~1164)
Tests: 1 passed, 0 failed, 0 skipped, 0 ignored, 398 filtered out

```

## Impact

Exceeding the group order increases vulner
During transaction signature verification, the chain id is not verified to be empty
Audit: 2024-09-kakarot-findings
Severity: MEDIUM

# Lines of code

https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/kakarot/eth_rpc.cairo#L250
https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/kakarot/accounts/library.cairo#L171


# Vulnerability details

## Impact

May cause problems with double spending

## Proof of Concept
When the evm transaction is executed, the `eth_send_raw_unsigned_tx` function verifies whether `tx.chain_id` is the same as the current `chain_id`.

But the problem is, if `chain_id` is empty, `chain_id` will not be validated:

```rust
@external
func eth_send_raw_unsigned_tx{
    syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*
}(tx_data_len: felt, tx_data: felt*) -> (
    return_data_len: felt, return_data: felt*, success: felt, gas_used: felt
) {
    alloc_locals;
    Pausable.assert_not_paused();
    let tx = EthTransaction.decode(tx_data_len, tx_data);

    // Validate chain_id for post eip155
    let (chain_id) = Kakarot.eth_chain_id();
@>  if (tx.chain_id.is_some != FALSE) {
        with_attr error_message("Invalid chain id") {
            assert tx.chain_id.value = chain_id;
        }
    }
    ....
}
```

If the transaction is of type `pre-eip155`, not verifying that `chain_id` is correct because `msg_hash` has been whitelisted:

```rust
    func execute_from_outside{
        syscall_ptr: felt*,
        pedersen_ptr: HashBuiltin*,
        bitwise_ptr: BitwiseBuiltin*,
        range_check_ptr,
    }(tx_data_len: felt, tx_data: felt*, signature_len: felt, signature: felt*, chain_id: felt) -> (
        response_len: felt, response: felt*
    ) {
        .....
        // Whitelisting pre-eip155
        let (is_authorized) = Account_authorized_message_hashes.read(msg_hash);
        if (pre_eip155_tx != FALSE) {
            with_attr error_message("Unauthorized pre-eip155 transaction") {
                assert is_authorized = TRUE;
            }
        }
        .....
    }

```

The problem is that an attacker or malicious user can set the signature type to `eip155` but use an empty `chain_id` so that the `chain_id` check can be skipped.


An attacker can set `tx_type` to 0, the value of `v` to greater than 28, and pre_eip155_tx = FALSE to avoid whitelisting:

```rust
  func execute_from_outside{
        syscall_ptr: felt*,
        pedersen_ptr: HashBuiltin*,
        bitwise_ptr: BitwiseBuiltin*,
        range_check_ptr,
    }(tx_data_len: felt, tx_data: felt*, signature_len: felt, signature: felt*, chain_id: felt) -> (
        response_len: felt, response: felt*
    ) {
        ......
        let r = Uint256(signature[0], signature[1]);
        let s = Uint256(signature[2], signature[3]);
        let v = signature[4];

        let tx_type = EthTransaction.get_tx_type(tx_data_len, tx_data);
        local y_parity: felt;
        local pre_eip155_tx: felt;
@>      if (tx_type == 0) {
@>          let is_eip155_tx = is_nn(28 - v);
            assert pre_ei
Insufficient Input Validation for evm_address in compute_starknet_address
Audit: 2024-09-kakarot-findings
Severity: MEDIUM

# Lines of code

https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/kakarot/account.cairo#L519


# Vulnerability details

## Proof of Concept
The function compute_starknet_address in the Account namespace uses the input evm_address to compute a Starknet address without validating the input. If an attacker provides a malformed or malicious evm_address, it could lead to incorrect Starknet addresses, which might be used for malicious purposes or cause the system to misbehave.

```
func compute_starknet_address{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}(
    evm_address: felt
) -> felt {
    ...
    let (hash_state_ptr) = hash_update_single{hash_ptr=pedersen_ptr}(
        hash_state_ptr=hash_state_ptr, item=evm_address
    );
    ...
}
```

Impact: This vulnerability could allow an attacker to bypass expected account computation mechanisms and create non-standard or incorrect addresses, leading to transaction failures or unauthorized contract deployment.

(PoC):
```
def test_invalid_evm_address():
    invalid_address = -1  # Invalid input for evm_address
    result = compute_starknet_address(invalid_address)
    assert result == 0  # Expected failure for invalid input

```

## Recommended Mitigation Steps
Add input validation for the evm_address to ensure it is within the valid address range.
```
if evm_address < MIN_ADDRESS or evm_address > MAX_ADDRESS {
    // Handle error
}
```


## Assessed type

Invalid Validation
Unbounded Payload Size in L2 to L1 Communication Enables Gas Exhaustion Attacks and Unprocessable Messages
Audit: 2024-09-kakarot-findings
Severity: MEDIUM

# Lines of code

https://github.com/kkrt-labs/kakarot-lib/blob/c2c7cb400f85c3699a6902946bcf4428d5b4fc61/src/CairoLib.sol#L185


# Vulnerability details

## Description

The sendMessageToL1 function in CairoLib lacks payload size validation, creating a vulnerability where arbitrarily large messages can be sent from L2 to L1. This exposes both layers to gas exhaustion risks through uncontrolled memory expansion during message processing. Without proper bounds, the function could consume excessive gas on L2 and potentially create unprocessable messages on L1, affecting the reliability of the cross-layer messaging system.

## Code Snippet

```solidity
function sendMessageToL1(bytes memory payload) internal {
    (bool success,) = CAIRO_MESSAGE_PRECOMPILE.call(payload);
    require(success, "CairoLib: sendMessageToL1 failed");
}
```

This current implementation makes a direct call to `CAIRO_MESSAGE_PRECOMPILE` (0x0000000000000000000000000000000000075002) with an unbounded payload. The precompile is responsible for handling the cross-layer message passing between Starknet (L2) and Ethereum (L1). However, without size validation, this creates several critical issues in the messaging system:

1. Quadratic gas scaling with payload size
2. Unpredictable memory expansion costs
3. Potential for gas exhaustion attacks
4. Risk of creating unprocessable L1 messages

When the function processes a message, all payload data must be loaded into memory. The EVM charges gas for memory expansion quadratically, making large payloads exponentially more expensive. The lack of a size bound means the gas cost could unexpectedly spike based on payload size.

## Proposed Fix
A safer implementation would be:

```solidity
library CairoLib {
    // Conservative limit based on typical L1/L2 message processing constraints
    uint256 private constant MAX_MESSAGE_SIZE = 128 * 1024; // 128KB 

    function sendMessageToL1(bytes memory payload) internal {
        uint256 size = payload.length;
        require(size > 0, "CairoLib: empty message");
        require(
            size <= MAX_MESSAGE_SIZE,
            "CairoLib: message size exceeds maximum allowed"
        );

        // Calculate memory expansion cost
        uint256 words = (size + 31) / 32;
        uint256 memoryGas = (words * 3) + (words * words / 512);
        require(
            gasleft() > memoryGas * 2,
            "CairoLib: insufficient gas for memory expansion"
        );

        (bool success, bytes memory result) = CAIRO_MESSAGE_PRECOMPILE.call(payload);
        if (!success) {
            assembly {
                revert(add(32, result), mload(result))
            }
        }
    }
}
```

The fix introduces several crucial safety mechanisms while staying within the library's context:
- Static size limit based on practical L1/L2 message processing constraints
- Memory expansion gas calculation and validation
- Proper error propagation from the precompile
- Empty message check to prevent wasteful operation
Mitigating Caller Address Risks in StarkNet’s `eth_send_transaction` Function
Audit: 2024-09-kakarot-findings
Severity: MEDIUM

# Lines of code

https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/kakarot/eth_rpc.cairo#L203
https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/kakarot/eth_rpc.cairo#L313


# Vulnerability details

## Summary 
In the `eth_send_transaction` function of StarkNet’s `eth_rpc.cairo` contract, using `get_caller_address()` can return a 0 address if the contract is called directly. This could lead to various issues, including invalid origin address generation, nonce retrieval errors, unintended transaction failures, security bypasses, and unintended state changes. Adding a check to validate that `caller_address` is not 0 at the start of the function can mitigate these risks. This enhances the security and reliability of the contract, ensuring that only valid transactions are processed and preventing unauthorized actions or unintended state changes.

## Proof of Concept
The `eth_send_transaction` function of the eth_rpc.cairo contract allows performing Ethereum-style transactions on StarkNet. It uses the `get_caller_address` function to get the caller.

[kakarot/src/kakarot/eth_rpc.cairo:eth_send_transaction-L203](https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/kakarot/eth_rpc.cairo#L203)
```cairo
        // @notice The eth_send_transaction function as described in the spec,
        //         see https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sendtransaction
        // @dev "nonce" parameter is taken from the corresponding account contract
        // @param to The address the transaction is directed to.
        // @param gas_limit Integer of the gas provided for the transaction execution
        // @param gas_price Integer of the gas price used for each paid gas
        // @param value Integer of the value sent with this transaction
        // @param data_len The length of the data
        // @param data Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI in the Solidity documentation
        // @param access_list_len The length of the access list
        // @param access_list The access list passed in the transaction
        // @return return_data_len The length of the return_data
        // @return return_data An array of returned felts
        // @return success A boolean, TRUE if the transaction succeeded, FALSE otherwise
        // @return gas_used The amount of gas used by the transaction
        func eth_send_transaction{
            syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, bitwise_ptr: BitwiseBuiltin*
        }(
            to: model.Option,
            gas_limit: felt,
            gas_price: felt,
            value: Uint256,
            data_len: felt,
            data: felt*,
            access_list_len: felt,
            access_list: felt*,
        ) -> (return_data_len: felt, return_data: felt*, success: felt, gas_used: felt) {
            alloc_locals;
SDIV Opcode Performs Unnecessary SMOD Computation Despite Only Needing Division
Audit: 2024-09-kakarot-findings
Severity: MEDIUM

# Lines of code

https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/kakarot/instructions/stop_and_math_operations.cairo#L198


# Vulnerability details



## Impact
When executing the SDIV opcode, Kakarot performs both division and modulo calculations internally, even though only the division result is needed. While the gas cost (GAS.LOW = 5) correctly matches Ethereum's specification, the implementation is computationally inefficient as it performs ~2x more computation than necessary. This results in:

- Computational overhead for every SDIV operation
- Reduced throughput for transactions using SDIV operations (Since Cairo steps are increased due to additional unnecessary logic)

*Note: the same applies for SMOD and other opcode that uses `uint256_unsigned_div_rem` function*

## Proof of Concept

The flow:
SDIV => `uint256_signed_div_rem` => `uint256_unsigned_div_rem`

```cairo
	SDIV:
	let range_check_ptr = [ap - 2];
	let popped = cast([ap - 1], Uint256*);

	let (quotient, _) = uint256_signed_div_rem(popped[0], popped[1]);

	tempvar bitwise_ptr = cast([fp - 7], BitwiseBuiltin*);
	tempvar range_check_ptr = range_check_ptr;
	tempvar result = Uint256(quotient.low, quotient.high);
	jmp end;
```

[stop_and_math_operations.cairo:L198](https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/kakarot/instructions/stop_and_math_operations.cairo#L198)


```cairo
func uint256_signed_div_rem{range_check_ptr}(a: Uint256, div: Uint256) -> (
    quot: Uint256, rem: Uint256
) {
    alloc_locals;
		.
		.
	 // Unsigned division.
    let (local quot, local rem) = uint256_unsigned_div_rem(a, div);
}
```
[uint256.cairo:L268](https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/utils/uint256.cairo#L268)


```cairo
func uint256_unsigned_div_rem{range_check_ptr}(a: Uint256, div: Uint256) -> (
	quotient: Uint256, remainder: Uint256
) {
	.
	.
local quotient: Uint256;
local remainder: Uint256;
%{
	a = (ids.a.high << 128) + ids.a.low
	div = (ids.div.high << 128) + ids.div.low
	quotient, remainder = divmod(a, div)

	ids.quotient.low = quotient & ((1 << 128) - 1)
	ids.quotient.high = quotient >> 128
	ids.remainder.low = remainder & ((1 << 128) - 1)
	ids.remainder.high = remainder >> 128
%}
```
[uint256.cairo:L131](https://github.com/kkrt-labs/kakarot/blob/7411a5520e8a00be6f5243a50c160e66ad285563/src/utils/uint256.cairo#L131)


Step-by-step explanation:

- When a contract has SDIV:
	```solidity
	int256 result = a / b;  // SDIV opcode
	```

- Kakarot processes this by:
  - Loading operands `a` and `b`
  - Calling `uint256_signed_div_rem(a, b)`
  - Computing full signed division algorithm
  - Computing full modulo algorithm
  - Discarding modulo result
  - Returning only quotient
  - Charges correct gas (GAS.LOW)

- The modulo computation is completely unused but still performed for every SDIV operation.

This can be demonstrated with a simple contract:

```solidity
contract SDIVH