Submitted on Sat Jul 13 2024 10:02:26 GMT-0400 (Atlantic Standard Time) by @Minato7namikazi for Attackathon | Fuel Network
Report ID: #33170
Report type: Smart Contract
Report severity: Medium
Target: https://github.com/FuelLabs/sway-libs/tree/0f47d33d6e5da25f782fc117d4be15b7b12d291b
Impacts:
- Permanent freezing of funds
During my audit of the UFP (unsigned fixed-point number) implementations in the sway-libs , this logic vulnerability were identified. This could lead to incorrect calculations in any smart contracts or scripts relying on this function and cause massive losses.
impl Exponent for UFP128 {
fn exp(exponent: Self) -> Self {
let one = UFP128::from((1, 0));
let p2 = one / UFP128::from((2, 0));
let p3 = one / UFP128::from((6, 0));
let p4 = one / UFP128::from((24, 0));
let p5 = one / UFP128::from((120, 0));
let p6 = one / UFP128::from((720, 0));
let p7 = one / UFP128::from((5040, 0));
// common technique to counter losing sugnifucant numbers in usual approximation
let _res_minus_1 = exponent + exponent * exponent * (p2 + exponent * (p3 + exponent * (p4 + exponent * (p5 + exponent * (p6 + exponent * p7)))));
let res = one;
res
}
}
The function calculates _res_minus_1
correctly .. but then it ignores this calculation and simply returns one
. This means the exponential function will always return 1, regardless of the input.
-
Incorrect Financial Calculations : using the vulnerable exponential function for compound interest, option pricing, or yield calculations. With this bug, all such calculations would return 1, regardless of the input.
-
Some token distribution models or staking rewards systems use exponential decay or growth. This bug would make such systems non-functional, potentially: Distributing incorrect amounts of tokens
we should use the calculated result like
impl Exponent for UFP128 {
fn exp(exponent: Self) -> Self {
let one = UFP128::from((1, 0));
let p2 = one / UFP128::from((2, 0));
let p3 = one / UFP128::from((6, 0));
let p4 = one / UFP128::from((24, 0));
let p5 = one / UFP128::from((120, 0));
let p6 = one / UFP128::from((720, 0));
let p7 = one / UFP128::from((5040, 0));
// Common technique to counter losing significant numbers in usual approximation
let res_minus_1 = exponent + exponent * exponent * (p2 +
exponent * (p3 + exponent * (p4 +
exponent * (p5 + exponent * (p6 +
exponent * p7)))));
one + res_minus_1
}
}
- Now it's calculates the exponential function using a Taylor series approximation and should return the correct result. the function now adds 1 to the calculated
res_minus_1
to get the final result, as the original calculation was for (e^x - 1).
#[test]
fn PoC_exp() {
let one = UFP128::from_uint(1);
let res = UFP128::exp(one);
assert(res == UFP128::from((1, 0)));
let two = UFP128::from_uint(2);
let res = UFP128::exp(two);
assert(res == UFP128::from((1, 0)));
let three = UFP128::from_uint(3);
let res = UFP128::exp(three);
assert(res == UFP128::from((1, 0)));
let four = UFP128::from_uint(4);
let res = UFP128::exp(four);
assert(res == UFP128::from((1, 0)));
let five = UFP128::from_uint(5);
let res = UFP128::exp(five);
assert(res == UFP128::from((1, 0)));
let six = UFP128::from_uint(6);
let res = UFP128::exp(six);
assert(res == UFP128::from((1, 0)));
let seven = UFP128::from_uint(7);
let res = UFP128::exp(seven);
assert(res == UFP128::from((1, 0)));
let eight = UFP128::from_uint(8);
let res = UFP128::exp(eight);
assert(res == UFP128::from((1, 0)));
let nine = UFP128::from_uint(9);
let res = UFP128::exp(nine);
assert(res == UFP128::from((1, 0)));
}