Token-0x

First Flight #54
Beginner FriendlyDeFi
100 EXP
View results
Submission Details
Severity: medium
Valid

`ERC20Internals::_transfer` causes overflow in recepient's account balance and leads to loss of recepient's funds

Impact:H

Likelihood:H

Root + Impact

Description

  • Recipient's account balance is not correctly calculated when the balance overflows, showing an incorrect value.

function _transfer(address from, address to, uint256 value) internal returns (bool success) {
assembly ("memory-safe") {
if iszero(from) {
mstore(0x00, shl(224, 0x96c6fd1e))
mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
if iszero(to) {
mstore(0x00, shl(224, 0xec442f05))
mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
let ptr := mload(0x40)
let baseSlot := _balances.slot
mstore(ptr, from)
mstore(add(ptr, 0x20), baseSlot)
let fromSlot := keccak256(ptr, 0x40)
let fromAmount := sload(fromSlot)
mstore(ptr, to)
mstore(add(ptr, 0x20), baseSlot)
let toSlot := keccak256(ptr, 0x40)
let toAmount := sload(toSlot)
if lt(fromAmount, value) {
mstore(0x00, shl(224, 0xe450d38c))
mstore(add(0x00, 4), from)
mstore(add(0x00, 0x24), fromAmount)
mstore(add(0x00, 0x44), value)
revert(0x00, 0x64)
}
sstore(fromSlot, sub(fromAmount, value))
@> sstore(toSlot, add(toAmount, value))
success := 1
mstore(ptr, value)
log3(ptr, 0x20, 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, from, to)
}
}

Risk

Likelihood:

  • The issue occurs when transfer amount of tokens that lead to arithmetic overflow.

Impact:

  • Recipient's balance is incorrectly calculated, leading to loss of recipient's funds because after overflow the recipient's balance is less than it was previously.

Proof of Concept

Please, insert the following test to Token.t.sol:

function test_transfer_overflow() public {
address sender = makeAddr("sender");
address receiver = makeAddr("receiver");
uint256 amountToMint = type(uint256).max - 1;
deal(address(token), sender, amountToMint);
deal(address(token), receiver, amountToMint);
uint256 balanceSenderBefore = token.balanceOf(sender);
assertEq(balanceSenderBefore, amountToMint);
uint256 balanceReceiverBefore = token.balanceOf(receiver);
assertEq(balanceReceiverBefore, amountToMint);
//to cause overflow it will be enough to send 2 wei
uint256 amountToSend = 2;
vm.prank(sender);
vm.expectEmit(true, true, false, true);
emit Transfer(sender, receiver, amountToSend);
token.transfer(receiver, amountToSend);
uint256 balanceSenderAfter = token.balanceOf(sender);
uint256 balanceReceiverAfter = token.balanceOf(receiver);
assertEq(balanceSenderAfter, balanceSenderBefore - amountToSend);
assertEq(balanceReceiverAfter, 0);
// the receiver's balance after the transfer is less than the balance before the transfer
assertGt(balanceReceiverBefore, balanceReceiverAfter);
}

Recommended Mitigation

Yul does not natively checks on overflow, so the check should be added:

function _transfer(address from, address to, uint256 value) internal returns (bool success) {
assembly ("memory-safe") {
if iszero(from) {
mstore(0x00, shl(224, 0x96c6fd1e))
mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
if iszero(to) {
mstore(0x00, shl(224, 0xec442f05))
mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
let ptr := mload(0x40)
let baseSlot := _balances.slot
mstore(ptr, from)
mstore(add(ptr, 0x20), baseSlot)
let fromSlot := keccak256(ptr, 0x40)
let fromAmount := sload(fromSlot)
mstore(ptr, to)
mstore(add(ptr, 0x20), baseSlot)
let toSlot := keccak256(ptr, 0x40)
let toAmount := sload(toSlot)
if lt(fromAmount, value) {
mstore(0x00, shl(224, 0xe450d38c))
mstore(add(0x00, 4), from)
mstore(add(0x00, 0x24), fromAmount)
mstore(add(0x00, 0x44), value)
revert(0x00, 0x64)
}
sstore(fromSlot, sub(fromAmount, value))
- sstore(toSlot, add(toAmount, value))
+ let result := add(toAmount, value)
+ if lt(result, toAmount) {
+ revert(0, 0) // Revert if overflow occurred
+ }
+ sstore(toSlot, result)
success := 1
mstore(ptr, value)
log3(ptr, 0x20, 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, from, to)
}
}
Updates

Lead Judging Commences

gaurangbrdv Lead Judge 19 days ago
Submission Judgement Published
Validated
Assigned finding tags:

overflow & underflow

overflow & underflow occurs

Appeal created

valya Submitter
18 days ago
gaurangbrdv Lead Judge
17 days ago
valya Submitter
17 days ago
gaurangbrdv Lead Judge
17 days ago
valya Submitter
17 days ago
gaurangbrdv Lead Judge 14 days ago
Submission Judgement Published
Validated
Assigned finding tags:

overflow & underflow

overflow & underflow occurs

Support

FAQs

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

Give us feedback!