Advent of Bugs Day 22: Rent is not linear
One simple mistakes that devs make when calculating the rent of accounts is to assume that rent is linear.
The assumption is that 10 bytes cost X rent, and 20 bytes in the same account therefore must cost 2X rent.
However this isn’t correct, because we have a base rent that needs to be paid even for accounts with 0 bytes of data.
This means that rent(0) = β (base rent).
And this implicitly means that when we combine two accounts into one, or we want to extend one account with additional data of size diff, to calculate the new rent, we should not do rent(old_size) + rent(diff).
Because imagine diff is 0, this would yield us rent(old_size) + β, meaning we get charged more for 0 added data.
Instead, we need to calculate rent(old_size + diff). This way, β will only get taken into account once, as intended.
-@dr497_ and @r0bre
Advent of Bugs #4: Bananas and Oranges
Everybody knows you can't add bananas and oranges together. The same principle holds for smart contracts, you can't mix quote and base currencies. Except you can when they're all u64.
This bug occurred in an AMM swap function with 4 code paths:
- Buy ExactIn
- Buy ExactOut
- Sell ExactIn
- Sell ExactOut
The first paths look fine. The code is repetitive, so you're tempted to assume the rest is correct too.
But Sell/ExactIn was comparing base amounts to quote amounts, like comparing bananas with oranges. The slippage check would pass even when it shouldn't.
Why did this survive 3 audits? Base and Quote are both stored as u64, so no compile-time or runtime error when comparing different units
A good way to prevent these bugs is to use newtype structs instead of type aliases to represent these different units in arithmetic operations.
- @dr497_
It's easy to fall for rent(a + b) = rent(a) + rent(b), however this is not true. Rent is not a linear function on Solana due to per-account overhead costs.
In some cases you'll just over-allocate the account (like in the example from above, coming from a multi-audited mainnet program), but in other cases it might lead to the account being underfunded.
🎄 Advent of Bugs 2024
'Tis the season for security bugs! Starting December 1st, we're unwrapping 24 interesting vulnerabilities we found auditing Solana programs this year. Think of it as an advent calendar, but instead of chocolate, you get real bugs.
Broken account checks, logic flaws, math errors, SVM footguns... grab some hot cocoa and join us for daily lessons from the trenches.
24 bugs. 24 days. First gift drops tomorrow 🎁
- Your Accretion Team: @brymko, @0xmahdirostami, @dr497_ , @r0bre
https://t.co/ZO2BVTIH30
TL;DR: (1) Missing access control on resize function (2) anyone could delete your order history (3) replay signed message orders
The article also includes a framework for thinking about what I call "degrees of freedom" in Solana programs to prevent similar bugs.
The replay protection looks solid... wait, who can resize this list?
My new blog article dives into a subtle access control bug in the @DriftProtocol that let an attacker replay your signed messages.
Full article & technical deep dive in comments ↓
🧵 The Difficulty of Enforcing Spending Limits
I found two spending limit issues in @OnSwig, both reveal interesting lessons about invariant enforcement that Solana dev should know about.
Let's dive in 👇
Key Takeaways:
- What can overflow WILL overflow → use checked arithmetic unless you have ironclad bounds
- Invariants enforced only at endpoints miss everything in between
- Multi-step transactions create subtle bypass opportunities
Similar pattern to the @kamino deposit cap bug.