Flow

Sablier
FoundryDeFi
20,000 USDC
View results
Submission Details
Severity: high
Invalid

Integer Overflow Vulnerability in balance Field. The balance field of the Stream struct is of type uint128.

Summary : The balance field of the Stream struct is of type uint128. This means that the balance of the stream is expected to be within the range of uint128, which is a 128-bit unsigned integer.

If the balance is expected to be within the range of uint128, then using uint128 as the type for balance is appropriate. This ensures that the balance value cannot exceed the maximum value that can be represented by uint128.

However, if the balance can potentially exceed the range of uint128, it is recommended to use a larger data type like uint256 to avoid potential overflow issues. uint256 is a 256-bit unsigned integer and can represent a much larger range of values.

Using a larger data type like uint256 for balance allows you to handle larger balances without the risk of overflow. It is important to consider the potential range of values that the balance can take and choose a data type that can accommodate that range.

Vulnerability Details : Changing the type of balance to uint256 would provide more flexibility and allow for larger balances.

Description: The balance field in the Stream struct is defined as a uint128 type, which can overflow if the balance exceeds the maximum value that can be represented by uint128. This can lead to incorrect calculations and potentially result in financial losses for users.

Affected Code:

struct Stream {
// slot 0
uint128 balance;
UD21x18 ratePerSecond;
//@Question What is slot?
// slot 1
address sender;
uint40 snapshotTime;
bool isStream;
bool isTransferable;
bool isVoided;
// slot 2
IERC20 token;
uint8 tokenDecimals;
// slot 3
uint256 snapshotDebtScaled;
}

Vulnerable Functionality: The balance field is used in various calculations throughout the contract, including:

  • Calculating the total balance of a stream

  • Updating the balance of a stream after a deposit or withdrawal

  • Checking if a stream has sufficient balance to perform a certain action

Attack Vector: An attacker can exploit this vulnerability by manipulating the balance field to overflow, potentially allowing them to:

  • Drain the balance of a stream

  • Perform unauthorized actions on a stream

  • Gain an advantage over other users

Exploitation: To exploit this vulnerability, an attacker would need to find a way to overflow the balance field, potentially by:

  • Depositing a large amount of tokens into a stream

  • Withdrawing a large amount of tokens from a stream

  • Manipulating the balance field directly through a vulnerable function

Mitigation: To mitigate this vulnerability, it is recommended to use a larger integer type, such as uint256, to represent the balance field. This would provide a much larger range of values and make it more difficult for an attacker to overflow the field.

Impact :

Impact on Protocol Functionality:

  1. Limited Balance Range: Using uint128 for the balance field limits the maximum balance that can be represented to 2^128 - 1. This may not be sufficient for protocols that require larger balances.

  2. Overflow Risk: If the balance exceeds the maximum value that can be represented by uint128, it will overflow and wrap around to a smaller value. This can lead to incorrect calculations and potentially result in financial losses for users.

  3. Inaccurate Calculations: Using uint128 for the balance field may lead to inaccurate calculations, especially when performing arithmetic operations that involve large numbers.

Impact on Protocol Security:

  1. Integer Overflow Vulnerability: Using uint128 for the balance field introduces an integer overflow vulnerability, which can be exploited by attackers to manipulate the balance and potentially gain an advantage.

  2. Replay Attacks: If an attacker can manipulate the balance to overflow, they may be able to perform replay attacks, which can result in financial losses for users.

Impact on Protocol Scalability:

  1. Limited Scalability: Using uint128 for the balance field may limit the scalability of the protocol, as it may not be able to handle large balances or rapid growth.

  2. Increased Gas Costs: Using uint128 for the balance field may result in increased gas costs, as the protocol may need to perform additional calculations to handle overflow and ensure accuracy.

Impact on Protocol Usability:

  1. User Experience: Using uint128 for the balance field may result in a poor user experience, as users may encounter errors or inconsistencies when performing transactions or checking their balances.

  2. Limited Functionality: Using uint128 for the balance field may limit the functionality of the protocol, as it may not be able to support certain features or use cases that require larger balances.

Proof of Concept Code : Here's a proof of concept code that demonstrates the potential impact of using uint128 for the balance field..

pragma solidity ^0.8.0;
contract Stream {
struct StreamData {
uint128 balance;
uint128 ratePerSecond;
uint256 startTime;
uint256 endTime;
}
mapping(address => StreamData) public streams;
function createStream(address sender, uint128 balance, uint128 ratePerSecond, uint256 startTime, uint256 endTime) public {
streams[sender] = StreamData(balance, ratePerSecond, startTime, endTime);
}
function updateBalance(address sender) public {
StreamData memory stream = streams[sender];
uint256 timeElapsed = block.timestamp - stream.startTime;
uint256 newBalance = stream.balance + (stream.ratePerSecond * timeElapsed);
// Check for overflow
if (newBalance > type(uint128).max) {
// Handle overflow
newBalance = type(uint128).max;
}
streams[sender].balance = uint128(newBalance);
}
function getBalance(address sender) public view returns (uint128) {
return streams[sender].balance;
}
}

This contract has three functions:

  • createStream: Creates a new stream with the specified parameters.

  • updateBalance: Updates the balance of the specified stream based on the rate per second and time elapsed.

  • getBalance: Returns the current balance of the specified stream.

The updateBalance function checks for overflow when calculating the new balance. If the new balance exceeds the maximum value that can be represented by uint128, it sets the balance to the maximum value.

Here's an example of how you can test this contract:

contract TestStream {
Stream public stream;
constructor() public {
stream = new Stream();
}
function testOverflow() public {
// Create a new stream with a large balance
stream.createStream(address(this), 2**128 - 1, 1, block.timestamp, block.timestamp + 1 hours);
// Update the balance
stream.updateBalance(address(this));
// Check the balance
uint128 balance = stream.getBalance(address(this));
// Assert that the balance is equal to the maximum value
assert(balance == type(uint128).max);
}

This test creates a new stream with a large balance, updates the balance, and checks that the balance is equal to the maximum value.

Tools Used : VS code

Recommendations :

1: Use a Larger Integer Type

Use a larger integer type, such as uint256, to represent the balance field. This will provide a much larger range of values and make it more difficult for an attacker to overflow the field.

Update the balance field to use a uint256 type:

struct Stream {
// slot 0
uint128 balance;
UD21x18 ratePerSecond;
//@Question What is slot?
// slot 1
address sender;
uint40 snapshotTime;
bool isStream;
bool isTransferable;
bool isVoided;
// slot 2
IERC20 token;
uint8 tokenDecimals;
// slot 3
uint256 snapshotDebtScaled;
}

Recommendation 2: Implement Additional Checks and Balances

  • Implement additional checks and balances to prevent overflow and ensure the integrity of the system.

  • Consider using a library like OpenZeppelin's SafeMath to handle arithmetic operations and prevent overflow.

  • Add checks to ensure that the balance field does not exceed the maximum value that can be represented by uint256.

Recommendation 3: Perform Thorough Testing and Auditing

  • Perform thorough testing and auditing to ensure the protocol is secure and functions as intended.

  • Test the protocol with a variety of inputs and scenarios to ensure that it handles overflow correctly.

  • Consider hiring a third-party auditor to review the protocol and identify any potential vulnerabilities.

Recommendation 4: Review Calculations and Comparisons

  • Review all calculations and comparisons involving the balance field to ensure they are correct and do not rely on the uint128 type.

  • Update any calculations or comparisons that rely on the uint128 type to use the uint256 type instead.

Recommendation 5: Consider Using a Different Data Type

  • Consider using a different data type, such as a fixed-point number or a decimal type, to represent the balance field.

  • Research and evaluate different data types to determine which one is best suited for the protocol's needs.

Updates

Lead Judging Commences

inallhonesty Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.