Skip to main content
Sigvex

Readonly CPI Write Bypass Remediation

How to fix readonly account write bypass via CPI.

Readonly CPI Write Bypass Remediation

Overview

Related Detector: Readonly CPI Write Bypass

Readonly accounts can be modified by CPI target programs that own them. The fix is to either require writable for accounts forwarded to CPI, or reload and re-validate account data after CPI returns.

Impact. A program that reads an account’s balance, owner, or invariant, makes a CPI, and then enforces a check against the pre-CPI snapshot is trusting data the callee may have changed. An attacker whose program is the CPI target mutates the account mid-transaction and the caller validates against the stale state — turning a “readonly” assumption into an exploitable window that can defeat balance or authorization checks.

// Option 1: Require writable
require!(account.is_writable, InvalidArgument);
invoke(&ix, &[account.clone()])?;

// Option 2: Re-validate after CPI
invoke(&ix, &[account.clone()])?;
ctx.accounts.my_account.reload()?;
validate_invariants(&ctx.accounts.my_account)?;

Common Mistakes

Mistake: Trusting Pre-CPI State

let balance = account.lamports();
invoke(&external_ix, &[account.clone()])?;
// WRONG: balance may have changed
require!(balance >= minimum, Insufficient);

Always re-read account state after CPI.

References