TSender

Cyfrin
DeFiFoundry
15,000 USDC
View results
Submission Details
Severity: high
Invalid

Assembly code in `TSender::airdropERC20` incorrectly assumes the transfer was successful

Summary

The original implementation of the token transfer function in the assembly code fails to correctly handle scenarios where the token contract's transfer function returns false.

Vulnerability Details

if iszero(call(gas(), tokenAddress, 0, 0x00, 0x64, 0, 0)) {
mstore(0x00, 0xfa10ea06) // cast sig "TSender__TransferFailed()"
revert(0x1c, 0x04)
}

The call opcode sends a message call to the token contract's transfer function and checks if the call was successful using the iszero function. However, this only checks if the call did not encounter an exception (e.g., out of gas or revert) and does not verify the actual return value of the transfer function.

If the transfer function returns false (indicating a failed transfer), the call still returns 1, meaning the assembly code incorrectly assumes the transfer was successful.

Impact

The contract may proceed under the false assumption that the token transfer was successful when it was not. This can lead to:

  1. Incorrect state updates in the contract.

  2. Potential loss of funds or tokens if subsequent operations rely on the successful transfer.

Tools Used

Menual review

Recommendations

To mitigate this issue, the assembly code should be enhanced to explicitly check the return value of the transfer function. Here is an example approach.

assembly {
// Perform the call and capture the success status
success := call(gas(), tokenAddress, 0, 0x00, 0x64, 0, 0)
// Get the size of the returned data
let size := returndatasize()
// Allocate memory for the returned data
let data := mload(0x40)
// Update the free memory pointer to allocate the returned data
mstore(0x40, add(data, and(add(size, 0x3f), not(0x1f))))
// Set the length of the returned data
mstore(data, size)
// Copy the returned data to the allocated memory
returndatacopy(add(data, 0x20), 0, size)
// Load the returned data into a register (assuming data is at offset 0x20)
let returnedData := mload(add(data, 0x20))
// Convert the returned data to a boolean value
let dataCheck := iszero(iszero(returnedData))
// Check if success is true and dataCheck is false
if success {
if iszero(dataCheck) {
// Revert with custom error message
mstore(0x00, 0x64b72627) // Custom error signature
revert(0x1c, 0x04)
}
}
}

The nested if statement checks if the call was successful and if the returned data is false. If both conditions are met, it reverts the transaction with a custom error message.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue
topg Submitter
about 1 year ago
patrickalphac Auditor
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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