uint to int Conversion
Overview
- Severity: Low
- Confidence: Medium
- Affected Versions: All
What is the uint to int Conversion vulnerability?
Solidity supports both signed and unsigned integers at various bit widths, for example int32
or uint128
. When casting from an unsigned to a signed type, even at the same bit width, there is a possibility that large values will silently overflow due to bit truncation, as signed types require a sign bit. For this reason, such casts should either be checked or avoided if program logic permits.
Further reading: Solidity Documentation: Integers
Technical example of vulnerable code
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
contract TokenManager {
// Mapping of user addresses to their token balances
mapping(address => uint256) public balances;
address owner;
constructor() {
owner = msg.sender;
}
// Event to log balance adjustments
event BalanceAdjusted(address user, int256 adjustment);
// Function to add tokens to a user's balance
function addTokens(address user, uint256 amount) public {
balances[user] += amount;
}
// Function to adjust a user's balance. Adjustments can be positive or negative.
function adjustBalance(address user, int256 adjustment) public {
require(msg.sender == owner, "Only the owner may make balance adjustments.");
balances[user] = uint256(int256(balances[user]) + adjustment);
emit BalanceAdjusted(user, adjustment);
}
// Additional functionality for token management omitted for brevity
}
In the above example, contract TokenManager
represents a portion of a DeFi system that can manage user tokens, e.g. ERC-20 tokens. It provides a function, adjustBalance()
, that only the contract owner can use to manually adjust token balances (for example, if something were recorded erroneously elsewhere in the system). In order to perform the adjustment with a signed integer, the user balance is cast from an unsigned integer, which could possibly overflow silently.
Technical example of how to fix the vulnerability
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
contract TokenManagerUpdated {
// Mapping of user addresses to their token balances
mapping(address => uint256) public balances;
address owner;
constructor() {
owner = msg.sender;
}
// Event to log balance adjustments
event BalanceAdjusted(address user, uint256 adjustment, bool negative);
// Function to add tokens to a user's balance
function addTokens(address user, uint256 amount) public {
balances[user] += amount;
}
// Function to adjust a user's balance. Adjustments can be positive or negative.
function adjustBalance(address user, uint256 adjustment, bool negative) public {
require(msg.sender == owner, "Only the owner may make balance adjustments.");
if (negative) {
balances[user] -= adjustment;
} else {
balances[user] += adjustment;
}
emit BalanceAdjusted(user, adjustment, negative);
}
// Additional functionality for token management omitted for brevity
}
In the revised example above, contract TokenManagerUpdated
has reworked the logic of the adjustBalance()
function to take an extra parameter indicating if the adjustment should be negative; thus, it can use only unsigned integers and avoid the cast in the previous example.