Sec3 (formerly Soteria) has a team of top experts in security audits of smart contracts. In the last few months, we’ve observed an increasing number of vulnerabilities in DeFi applications that fall into a common pattern: bidirectional rounding.
If a smart contract has a bidirectional function or functions (e.g., swap between a pair of tokens or mint/redeem a token) and the function uses the same rounding operation over arithmetic results in both directions, then the function is likely vulnerable to two-way trading attacks.
In this article, we explain the key issues of bidirectional rounding and show how Sec3 Pro automatically detects this type of rounding vulnerabilities.
The reason is simple: bidirectional rounding may allow attackers to exploit round-off errors to gain profit for free through round-trip trading:
1. trade X token1 for token2 => get Y token2
2. trade Y token2 for token1 => get X’ token1
3. profit == X’-X > 0
Although profit is small in each round-trip trade (steps 1–2), the round-trip may be repeated many times, eventually leading to significant losses to the protocol.
Let’s consider a real-world example of bidirectional rounding in the spl token-lending program (discovered by Neodyme originally, see “How to Become a Millionaire, 0.000001 BTC at a Time”):
The functions liquidity_to_collateral and collateral_to_liquidity are a pair of bidirectional functions, called by deposit_liquidity and redeem_collateralrespectively.
In each direction, given an input collateral_amount or liquidity_amount, the functions compute liquidity_amount or collateral_amount.
However, in both directions, the functions use the same rounding operation try_round_u64 internally:
Since rounding up or down can be controlled by the attacker through the inputs collateral_amount and liquidity_amount, a two-way swap can be made profitable.
The fixes to bidirectional rounding depend on the semantics of the code, but in most common cases can be simple:
The fixes above (i.e., adding to a floor() operation to amount_out and a ceil() operation to fee) ensure that amount_out is always rounded down and fee is always rounded up, thus a round-trip trade is no longer profitable.
For example, the fixes in spl token-lending apply a try_floor_u64() operation to ensure the result is always rounded down:
Note: bidirectional rounding vulnerabilities do not necessarily need two different functions, but can exist in a single function that handles two-way trading, such as swap.
Sec3 Pro is the premier security analysis service for Solana smart contracts.
To detect bidirectional rounding issues:
The tool first finds out all the bidirectional functions in the whole smart smart including dependencies. For each bidirectional function, it locates rounding signatures (e.g., often Integer multiplications followed by division) and if the result of any rounding operation is not consistently rounded up or down, then the tool flags a potential vulnerability.
The following shows a screenshot of bidirectional rounding reported by Sec3 Pro in our example:
Sec3 is founded by leading minds in the fields of blockchain security and software verification. Sec3’s mission is to create a decentralized future that is secure. Sec3 team is currently building a trustworthy platform for securing Solana projects.