Home Web3 SecuritySmart Contract Audit Solidity Security Vulnerability: Unchecked Return Values

Solidity Security Vulnerability: Unchecked Return Values

by ImmuneBytes
Solidity Security Vulnerability: Unchecked Return Values

The Underlying Issue

In Solidity, developers can perform external calls using various methods, like send(), call(), and transfer(). However, each of these has a different behavior when it comes to error handling:

  • send() and call() return a boolean value (true or false) indicating the success or failure of the operation. A common pitfall is failing to check this return value. If the external call fails, these methods will not revert the entire transaction but will simply return false.
  • transfer(), conversely, reverts the entire transaction if the external call fails, serving as a more failsafe method for transferring Ether.

Failure to properly handle these return values or understand the nuances between these methods can lead to vulnerabilities in the smart contract, opening up opportunities for exploits.

The Lotto Contract

Let’s look at the example below:

The Trap

The core issue lies in where the contract uses send() without checking its return value. If the external call fails due to a variety of possible reasons (e.g., gas limitations, malicious fallback functions), the variable payedOut would still be set to true.

This essentially allows anyone to withdraw the remaining funds using the withdrawLeftOver() function even if the winner has not actually received their reward.

Preventative Measures

  1. Use Transfer Over Send: Whenever possible, use transfer() instead of send() since the former reverts the transaction if the external call fails.
  2. Check Return Values: Always validate the return value of send() or call() functions to take appropriate actions if they return false.
  3. Adopt a Withdrawal Pattern: Employing a withdrawal pattern ensures that the end-user will call a separate function to complete the transaction, thus allowing the contract to handle external call failures more gracefully.
  4. Implement Event Logging: By using events, contracts can log specific activities. If a function like send() or call() fails, this event can be recorded. This will provide transparency and traceability.
  5. Gas Limit Considerations: Always ensure that there’s adequate gas for operations. Though send() and transfer() automatically use a stipend of 2300 gas, other operations might require more.

Conclusion

The unchecked return values vulnerability may seem trivial, but it can lead to significant consequences, including fund loss.

Developers should not underestimate the power of return values and should adopt best practices to safeguard their contracts against such vulnerabilities.

You may also like