Attackathon _ Fuel Network 32696 - [Smart Contract - High] incorrect setting of non_negative value i
Submitted on Sat Jun 29 2024 20:22:34 GMT-0400 (Atlantic Standard Time) by @zeroK for Attackathon | Fuel Network
Report ID: #32696
Report type: Smart Contract
Report severity: High
Target: https://github.com/FuelLabs/sway-libs/tree/0f47d33d6e5da25f782fc117d4be15b7b12d291b
Impacts:
Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
the function ceil in all of IFP libs used to return smallest number that is equal or greater than the self.underlying
, this function have crucial rule as it will be used in many Defi protocols that will built on fuel, however there is incorrect set of non_negative
value when this function called and the else
case run, as there is possibility of setting the non_negative
value to true when its false in specific case if ceil == UFP64::from(1)
but this value will not be set in the self when its returned and used by the third party or the round
function itself when calling the ceil function.
Vulnerability Details
let's take a look at the ceil
function in the IFP128:
pub fn ceil(self) -> Self {
let mut underlying = self.underlying;
let mut non_negative = self.non_negative;
if self.non_negative {
underlying = self.underlying.ceil();
} else {
let ceil = self.underlying.ceil();
if ceil != self.underlying {
underlying = ceil + UFP64::from(1);
if ceil == UFP64::from(1) {
non_negative = true; // in this situation non negative should be true
}
} else {
underlying = ceil;
}
}
Self {
underlying: underlying,
non_negative: self.non_negative,// but we set the self.non_negative which is not the updated value above (shown in POC)
}
}
}
as shown above when the ceil value we get is equal to UFP64::from(1)
then the non negative should be true, but in the Self we set the non negative value to the input that we use as self
which is false in our case, this can lead to critical issue for defi protocols when they use ceil function or round function which returns ceil in specific situation:
pub fn round(self) -> Self {
let mut underlying = self.underlying;
if self.non_negative {
underlying = self.underlying.round();
} else {
let floor = self.underlying.floor();
let ceil = self.underlying.ceil();
let diff_self_floor = self.underlying - floor;
let diff_ceil_self = ceil - self.underlying;
underlying = if diff_self_floor > diff_ceil_self {
floor
} else {
ceil //@audit return ceil Self value which sets incorrectly
};
}
Self {
underlying: underlying,
non_negative: self.non_negative,
}
}
}
we believe this can be high issue but it can be downgraded to medium as its logical issue if we saw it as issue in lib but critical issue if we think about how can cause damage to protocols directly. (we set this as medium and leave the judging to immunefi team for upgrading the impact level)
Impact Details
incorrect set of non_negative value in ceil can cause trouble when its called/used
References
while there is possibility of non_negative value to get updated then the Self.non_negative should be set to the let non_negative
variable
Proof of concept
Proof of Concept
run this test right below the ceil function in IFP128.sw by runing forc test
#[test]
fn test_ceil_function() {
// Set up a value that, when ceiled, becomes 1
let tiny_fractional_value = UFP64::from_uint(0) + UFP64::from(1); // Just below 1 in fixed-point
let negative_tiny_fractional_value = IFP128::from(tiny_fractional_value).sign_reverse();
// Ensure the value is negative
let mut underlying = negative_tiny_fractional_value.underlying;
let mut non_negative = negative_tiny_fractional_value.non_negative;
assert(underlying == negative_tiny_fractional_value.underlying);
assert(non_negative == negative_tiny_fractional_value.non_negative);
assert(negative_tiny_fractional_value.non_negative() == false);
// Calculate the ceiling value
let ceil = negative_tiny_fractional_value.underlying.ceil();
assert(ceil != underlying);
// Apply the ceil function
let res = negative_tiny_fractional_value.ceil();
// Check that non_negative is set to true due to ceil being 1
assert(ceil == UFP64::from_uint(1));
assert(res.non_negative != true); //this should be set to true not false but the implementation not work correctly
}
Last updated
Was this helpful?