Skip to content

Latest commit

 

History

History
105 lines (80 loc) · 3.71 KB

026.md

File metadata and controls

105 lines (80 loc) · 3.71 KB

Dandy Mahogany Buffalo

Medium

Edge cases in _rpow function resulting in incorrect results or reverts in unexpected scenarios

Summary

The _rpow function, used to compute exponentiation with rounding in assembly, is prone to precision loss and undefined behavior. The function performs repeated multiplications and divisions, which are susceptible to edge cases such as zero input values (x = 0) or excessively large exponents (n). Despite implementing checks to prevent overflow, these checks do not fully guard against all edge cases, resulting in incorrect results or reverts in unexpected scenarios.

Root Cause

The _rpow function is implemented as follows: https://github.com/sherlock-audit/2024-11-autonomint/blob/main/Blockchain/Blockchian/contracts/lib/BorrowLib.sol#L1038-L1060

function _rpow(uint x, uint n, uint b) public pure returns (uint z) {
  assembly {
    switch x case 0 {switch n case 0 {z := b} default {z := 0}}
    default {
      switch mod(n, 2) case 0 { z := b } default { z := x }
      let half := div(b, 2)  // for rounding.
      for { n := div(n, 2) } n { n := div(n,2) } {
        let xx := mul(x, x)
        if iszero(eq(div(xx, x), x)) { revert(0,0) }
        let xxRound := add(xx, half)
        if lt(xxRound, xx) { revert(0,0) }
        x := div(xxRound, b)
        if mod(n,2) {
          let zx := mul(z, x)
          if and(iszero(iszero(x)), iszero(eq(div(zx, x), z))) { revert(0,0) }
          let zxRound := add(zx, half)
          if lt(zxRound, zx) { revert(0,0) }
          z := div(zxRound, b)
        }
      }
    }
  }
}
  1. Edge case for x = 0: When x = 0, computations such as let xx := mul(x, x) result in xx = 0. While some cases are covered by the switch x logic, the subsequent arithmetic operations may still lead to undefined behavior.

  2. Exponentiation with large n: Iterative squaring (x := mul(x, x)) and dividing operations can lead to precision loss or undefined values due to the finite size of uint.

For example:

Input: _rpow(0, 3, 10) Expected Behavior: 0 Actual Behavior: Potential revert or incorrect result.

Internal pre-conditions

No response

External pre-conditions

No response

Attack Path

No response

Impact

  1. Results in invalid calculations for contracts that rely on precise exponentiation, potentially causing financial loss or unintended behavior.
  2. A contract invoking _rpow with edge-case inputs may revert, leading to DoS for critical operations.
  3. Improper rounding or incorrect values may lead to mismanagement of funds in financial applications, affecting protocol integrity.

PoC

const { ethers } = require("hardhat");
const { expect } = require("chai");

describe("Testing rpow function", function () {
  let contract;

  before(async () => {
    const Rpow = await ethers.getContractFactory("RpowContract"); // Replace with actual contract name
    contract = await Rpow.deploy();
    await contract.deployed();
  });

  it("should handle x=0 and n > 0", async () => {
    const result = await contract._rpow(0, 3, 10);
    console.log("Result:", result.toString());
    expect(result).to.equal(0); // Expected behavior
  });

  it("should handle large n", async () => {
    const result = await contract._rpow(2, 1e6, 1e18);
    console.log("Result for large n:", result.toString());
  });
});

Output: For _rpow(0, 3, 10): Reverts or returns incorrect value. For _rpow(2, 1e6, 1e18): Causes unexpected behavior, such as incorrect results due to precision loss.

Mitigation

  1. Explicitly handle x = 0 and n = 0 at the beginning of the function. Reject excessively large values of n to prevent precision loss.
  2. Use safe math libraries for all arithmetic operations, even in assembly.