Overflow and Underflow Vulnerabilities in Cairo

16 August, 2024
article image
Expert Insights

Contents

Overflow and Underflow Vulnerabilities in Cairo

Intro

In this article, we will explore one of the most common categories of vulnerabilities in the Cairo language: overflow and underflow. By comparing the approaches of two Cairo versions, 0.x and 1.0, to addressing this issue, we will analyze how each of them handles this vulnerability.

Cairo Evolution

In 2020, StarkWare introduced Cairo 0, a fully functional programming language for creating verifiable computations. Originating as an assembly language, Cairo gradually evolved, expanding its capabilities.

Initially, Cairo 0.x required developers to have a deep understanding of cryptographic primitives and architecture, making it challenging to learn. With the advent of Cairo 1, the situation has changed. The language became more abstract, allowing developers to focus on code logic rather than the intricacies of the architecture.

Cairo 1 delivers high performance through the Rust virtual machine and enhances program security through the ability to create verifiable computations.

Overflow & Underflow Vulnerability

In Cairo, overflow and underflow issues can arise during arithmetic operations involving felt elements (the fundamental data type). These issues occur when the result of the operation falls outside the valid range.

Cairo 0.x

Felt elements are essentially integers, but with some unique properties. They wrap around within a specific range determined by a large prime number (P). The range of valid values for a felt is from 0 to P-1, where P is a prime number that is 252 bits long (felt252).

Overflow occurs when the result is greater than or equal to P. Similar to overflow, underflow arises when the result of an arithmetic operation on felt elements is less than 0.

This implies that if you add two numbers and the result exceeds this maximum value, Cairo will wrap the result back into the range rather than throwing an error. This behavior can lead to unexpected results:

fn overflow_felt252() -> felt252 {
    // Assign max felt252 value = 2^251 + 17 * 2^192
    let max: felt252 = 3618502788666131106986593281521497120414687020801267626233049500247285301248 + 17 * 6277101735386680763835789423207666416102355444464034512896;
    max + 3
}

fn underflow_felt252() -> felt252 {
    let min: felt252 = 0;
    // Assign max felt252 value = 2^251 + 17 * 2^192
    let substract = (3618502788666131106986593281521497120414687020801267626233049500247285301248 + 17 * 6277101735386680763835789423207666416102355444464034512896);
    min - substract
}

As a result, we will get the wrong values:

Untitled

Source: https://book.starknet.io/ch02-13-04-security-considerations.html#4-handling-overflow-and-underflow-in-smart-contracts

Cairo 1.0

Cairo 1.0 introduces built-in support for secure integer types, such as u128 and u256, which automatically handle overflows and underflows. By utilizing these types, transactions will be rolled back if an overflow is detected, preventing erroneous outcomes.

An example of using the u128 data type to handle overflow and underflow:

fn overflow_u128() -> u128 {
    let max: u128 = 0xffffffffffffffffffffffffffffffff_u128; // Assign max u128 value
    (max + 3_u128
}

fn underflow_u128() -> u128 {
    let min: u128 = 0_u128;
    min - 3_u128
}

If an overflow or underflow occurs, the transaction will be reverted with a corresponding failure reason:

Untitled

Source: https://book.starknet.io/ch02-13-04-security-considerations.html#4-handling-overflow-and-underflow-in-smart-contracts

  • Failure reasons for u128:

    • 0x753132385f616464204f766572666c6f77=u128_add Overflow
    • 0x753132385f737562204f766572666c6f77=u128_sub Overflow

Similarly, the u256 data type can be used to handle overflow and underflow:

fn overflow_u256() -> u256 {
    let max_u128: u128 = 0xffffffffffffffffffffffffffffffff_u128;
    let max: u256 = u256 { low: max_u128, high: max_u128 }; // Assign max u256 value
    let three: u256 = u256 { low: 3_u128, high: 0_u128 }; // Assign 3 value
    max + three
}

fn underflow_u256() -> u256 {
    let min: u256 = u256 { low: 0_u128, high: 0_u128 }; // Assign 0 value
    let three: u256 = u256 { low: 3_u128, high: 0_u128 }; // Assign 3 value
    min - three
}

Executing these functions will cause the transaction to return if an overflow is detected:

Untitled Source: https://book.starknet.io/ch02-13-04-security-considerations.html#4-handling-overflow-and-underflow-in-smart-contracts

  • Failure reasons for u256:

    • 0x753235365f616464204f766572666c6f77=u256_add Overflow
    • 0x753235365f737562204f766572666c6f77=u256_sub Overflow

Security Recommendations

As Cairo is a relatively new language, adopting well-established practices can significantly enhance code quality and security:

  1. OpenZeppelin Contracts for Cairo https://github.com/OpenZeppelin/cairo-contracts
  2. Static-analyzer tool for Starknet smart contracts https://github.com/crytic/caracal
  3. Compile smart contracts with a newer version of the Cairo compiler.
  4. Keep your code regularly updated to avoid potential vulnerabilities.

More great resources:

  1. Cairo 1 Workshop https://github.com/starknet-edu/starknet-cairo-101
  2. List of Starknet security resources, tools, CTFs etc. https://github.com/amanusk/awesome-starknet-security?tab=readme-ov-file

Conclusion

The transition from Cairo 0 to Cairo 1.0 marked a significant simplification of the language and the introduction of important security features, making it more reliable and user-friendly. However, creating Cairo-contracts still requires deep Cairo knowledge and a serious approach to security auditing.

Telegram
Expert Insights

Contents

Telegram

Have a question?

Have a question?

Stay Connected with OXORIO

We're here to help and guide you through any inquiries you might have about blockchain security and audits.