Attackathon _ Fuel Network 32412 - [Smart Contract - Insight] the IFP divide functions does not have
Submitted on Thu Jun 20 2024 14:54:21 GMT-0400 (Atlantic Standard Time) by @zeroK for Attackathon | Fuel Network
Report ID: #32412
Report type: Smart Contract
Report severity: Insight
Target: https://github.com/FuelLabs/sway-libs/tree/0f47d33d6e5da25f782fc117d4be15b7b12d291b
Impacts:
Unbounded gas consumption
Block stuffing
Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
the function divide
in the ifp libs are used to divide two number, however this function mentioned that the function will panic when divisor is zero rather than revert with helpful error messgae, this can cause trouble for users interfaces in case of debugging. an assert
is implemented in UFP libs which revert in this case rather than panic the whole fuelVM.
Vulnerability Details
the divide
in IFP libs are implemented as below:
impl core::ops::Divide for IFP128 {
/// Divide a IFP128 by a IFP128. Panics if divisor is zero.
fn divide(self, divisor: Self) -> Self {
let non_negative = if (self.non_negative
&& !self.non_negative)
|| (!self.non_negative
&& self.non_negative)
{
false
} else {
true
};
Self {
underlying: self.underlying / divisor.underlying,
non_negative: non_negative,
}
}
}
as shown above the call will panic if the divisor is zero and no check implemented to revert with reason rather than panic similar to ufp libs:
impl core::ops::Divide for UFP64 {
/// Divide a UFP64 by a UFP64. Panics if divisor is zero.
fn divide(self, divisor: Self) -> Self {
let zero = UFP64::zero();
assert(divisor != zero);
let denominator = U128::from((0, Self::denominator()));
// Conversion to U128 done to ensure no overflow happen
// and maximal precision is avaliable
// as it makes possible to multiply by the denominator in
// all cases
let self_u128 = U128::from((0, self.underlying));
let divisor_u128 = U128::from((0, divisor.underlying));
// Multiply by denominator to ensure accuracy
let res_u128 = self_u128 * denominator / divisor_u128;
if res_u128.upper() != 0 {
// panic on overflow
revert(0);
}
Self {
underlying: res_u128.lower(),
}
}
}
Impact Details
the divide function panics when the divisor is zero rather than revert with helpful error message.
References
its recommended to avoid panics in fuelVM by using assert, implement the ifp libs similar to ufp libs.
Proof of concept
Proof of Concept
contract;
use sway_libs::fixed_point::{ifp128::IFP128, ifp256::IFP256, ifp64::IFP64,};
abi MyContract {
fn test_function();
}
impl MyContract for Contract {
fn test_function() {
let num = IFP128::from_uint(10u64);
let num2 = IFP128::from_uint(0);
IFP128::divide(num, num2);
}
}
#[test(should_revert)]
fn test_function_contract() {
let caller = abi(MyContract, CONTRACT_ID);
let result = caller.test_function {}();
}
Last updated
Was this helpful?