Unsafe unwrap
Description
- Vulnerability Category:
Validations and error handling
- Vulnerability Severity:
Minor
- Detectors:
unsafe-unwrap
- Test Cases:
unsafe-unwrap-1
In Rust, the unwrap
method is commonly used for error handling. It retrieves the inner value of an Option
or Result
. If an error or None
occurs, it calls panic!
without a custom error message.
The usage of unwrap
can lead to a panic and crash the program, which is not desired behavior in most cases, particularly in smart contracts.
Exploit Scenario
Consider the following ink!
contract:
#[ink::contract]
mod unsafe_unwrap {
use ink::storage::Mapping;
#[ink(storage)]
pub struct UnsafeUnwrap {
total_supply: Balance,
balances: Mapping<AccountId, Balance>,
}
// ...
impl UnsafeUnwrap {
/// Returns the balance of a given account.
#[ink(message)]
pub fn balance_of(&self, owner: AccountId) -> Balance {
self.balances.get(owner).unwrap()
}
// ...
}
}
In this contract, the balance_of
function uses the unwrap
method to retrieve the balance of an account. If there is no entry for this account in the balances mapping, the contract will panic and halt execution, potentially leading to malicious exploitation to disrupt the contract's operation.
The vulnerable code example can be found here
.
Remediation
Instead of using unwrap
, use a safer method for error handling. In this case, if there is no entry for an account in the balances
mapping, return a default value (like 0
).
#[ink::contract]
mod unsafe_unwrap {
use ink::storage::Mapping;
#[ink(storage)]
pub struct UnsafeUnwrap {
total_supply: Balance,
balances: Mapping<AccountId, Balance>,
}
// ...
impl UnsafeUnwrap {
/// Returns the balance of a given account.
#[ink(message)]
pub fn balance_of(&self, owner: AccountId) -> Balance {
self.balances.get(owner).unwrap_or(0)
}
// ...
}
}
The remediated code example can be found here
.