Vulnerabilities that aren't
The runtime patched them. The curriculum didn't.
Hey, it’s Arsen.
In today’s menu:
• Reentrancy, float drift, and unchecked CPIs — all noise in Solana
• The phishing email that passes DKIM, SPF, and DMARC
• How fixing one rounding error introduced another
• And more…
🕵🏼♂️ Vulnerability
The Solana security curriculum is still teaching bugs that don’t exist
You open a new Solana audit.
Your checklist: reentrancy, float non-determinism, unchecked CPI return values.
Which of these does the runtime actually care about?
None of them.
The most persistent security misinformation doesn’t come from obscure corners of the internet. It comes from official docs, recommended curricula, and the LLMs auditors use every day. These sources cite each other. Outdated content enters LLM training data. Those models generate new content. The loop closes.
Reentrancy: CPI depth is capped at four. More importantly, the runtime rejects any invocation that calls back up the call stack. The A→B→A loop that drains Ethereum contracts can’t form in Solana. Self-reentrancy (A→A) is technically possible, but it requires specific conditions and is genuinely rare.
Float non-determinism: There are no floating-point opcodes in SBPF. The VM emulates them through LLVM. Hardware differences don’t reach the application layer. Floats are still worth checking for NaN, negative infinity, and precision loss — but cross-architecture divergence isn’t the concern.
Unchecked CPI return values: A failing CPI reverts the entire transaction. There’s no return value to check. The whole transaction succeeds or nothing does. Developers rely on this property by design.
Partial state commitment: Same principle. If any instruction fails, all writes roll back. No in-between state.
load\_instruction\_at / load\_current\_index: Gone since 2022. Still appearing in boot camp curricula and LLM outputs.
Some of these bugs were real — years ago. Anchor and the Agave runtime closed most of them. The docs didn’t keep up.
The runtime improved.
The curriculum didn’t.
🗞️ News
A real email from noreply@robinhood.com — phishing CTA inside
The attacker’s email passes DKIM, SPF, and DMARC. It comes from noreply@robinhood.com.
What does your user do when an email passes every authentication check?
They trust it. They click.
Here’s the chain. Gmail ignores dots — v.ok@gmail.com and vok@gmail.com land in the same inbox. Robinhood treats them as different addresses. The attacker registers a new Robinhood account using the dot-trick version of their target’s email.
Then they rename their new device to a string of HTML.
Robinhood sends an “unrecognized activity” alert. The template renders the device name directly — no sanitization. The attacker’s HTML executes inside the official security email. A phishing CTA appears. The user sees a legitimate sender, passes every visual trust signal, and clicks a link the attacker controls.
Email authentication verifies the sender. It says nothing about the payload. SPF, DKIM, and DMARC are envelope-level checks. A properly signed email can still carry a fraudulent link.
This isn’t a new attack class. It’s a classic injection pattern targeting a high-trust channel. What makes it effective: security alert emails get acted on fast. Users don’t linger.
Robinhood patched the injection. But the pattern survives anywhere user-controlled input renders unsanitized in a transactional email template.
Real email. Real sender. Every auth check passing.
Still phishing.
📚 Education
Two floor operations. One slow drain on every depositor.
You’re auditing a Solana stake pool. Users deposit SOL, receive LP tokens, and burn them on withdrawal.
The withdrawal math converts LP tokens to lamports and rounds down. Protocol keeps the dust. Standard.
What happens when the fix introduces a second rounding operation going the other direction?
The team added a new withdrawal path that creates a PDA stake account. On Solana, every account needs rent to stay alive. Someone has to fund it.
The original code burned LP tokens based on full withdraw_lamports — the SOL amount including rent. If the user paid rent directly, they received split_lamports = withdraw_lamports − stake_rent but burned tokens worth withdraw_lamports. Overpay.
An auditor caught it. The fix: check if the user is the payer. If so, reverse-convert split_lamports back into LP tokens and burn that instead.
The fix is logically correct. It still introduced a bug.
LP tokens → lamports is floor division. Lamports → LP tokens is another floor division. Two sequential floors favor the same beneficiary — both round down. The protocol under-burns tokens on every withdrawal. Unbacked tokens accumulate in circulation. The exchange rate slowly decreases for every depositor in the pool.
The error per withdrawal is small — bounded by the ratio of token supply to total lamports. But it compounds. Every under-burn widens the ratio. A wider ratio increases the next under-burn.
The real fix was simpler: have the pool’s reserve stake account pay the rent. split_lamports equals withdraw_lamports. No reverse conversion. No second floor.
Every conversion function has a rounding direction.
Every rounding direction has a beneficiary.
When you audit a fix to financial logic, don’t just ask whether it resolved the original issue.
Ask whether it preserved the rounding invariants.
That’s it for this week.
Reply with the Solana bug, tool, or pattern you want me to cover next — I read every one.
If a working Solana auditor in your circle would find this useful, forward it their way.
— Arsen, working Solana auditor




