Skip to content

Commit c777217

Browse files
committed
fix(symbolic-spl): discharge AccountState discriminant branches
1 parent 8ed36a9 commit c777217

4 files changed

Lines changed: 124 additions & 0 deletions

File tree

kmir/src/kmir/kdist/mir-semantics/lemmas/kmir-lemmas.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ For symbolic enum values, the variant index remains unevaluated but the original
9494
requires isOneOf(DISCR, DISCRS)
9595
[simplification, symbolic(DISCR)]
9696
97+
// Normalize enum-discriminant comparisons into the same shape emitted by branch splits.
98+
rule #lookupDiscrAux(DISCRS, IDX) ==Int DISCR => DISCR ==Int #lookupDiscrAux(DISCRS, IDX)
99+
[simplification]
100+
97101
rule 0 <=Int asInt(#findVariantIdxAux(DISCR, DISCRS, _)) => true
98102
requires isOneOf(DISCR, DISCRS)
99103
[simplification, symbolic(DISCR)]

kmir/src/kmir/kdist/mir-semantics/symbolic/spl-token.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,9 @@ The `#initBorrow` helper resets borrow counters to 0 and sets the correct dynami
362362
andBool #isSplPubkey(?SplDelegateKey)
363363
andBool 0 <=Int ?SplAmount andBool ?SplAmount <Int (1 <<Int 64)
364364
andBool 0 <=Int ?SplAccountState andBool ?SplAccountState <=Int 2
365+
andBool (0 ==Int #lookupDiscrAux(discriminant(0) discriminant(1) discriminant(2) .Discriminants, variantIdx(?SplAccountState))
366+
orBool 1 ==Int #lookupDiscrAux(discriminant(0) discriminant(1) discriminant(2) .Discriminants, variantIdx(?SplAccountState))
367+
orBool 2 ==Int #lookupDiscrAux(discriminant(0) discriminant(1) discriminant(2) .Discriminants, variantIdx(?SplAccountState)))
365368
andBool 0 <=Int ?SplDelegatedAmount andBool ?SplDelegatedAmount <Int (1 <<Int 64)
366369
andBool 0 <=Int ?SplIsNativeLamportsVariant andBool ?SplIsNativeLamportsVariant <=Int 1
367370
andBool (0 ==Int #lookupDiscrAux(discriminant(0) discriminant(1) .Discriminants, variantIdx(?SplIsNativeLamportsVariant)) orBool 1 ==Int #lookupDiscrAux(discriminant(0) discriminant(1) .Discriminants, variantIdx(?SplIsNativeLamportsVariant)))
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use std::cell::RefCell;
2+
use std::convert::TryInto;
3+
use std::rc::Rc;
4+
5+
type ProgramResult = Result<(), ProgramError>;
6+
7+
fn main() {
8+
let _keep: fn(&AccountInfo<'_>) -> ProgramResult = repro;
9+
}
10+
11+
#[no_mangle]
12+
pub fn repro(source_account_info: &AccountInfo<'_>) -> ProgramResult {
13+
cheatcode_is_spl_account(source_account_info);
14+
let account = get_account(source_account_info)?;
15+
16+
if account.state == AccountState::Frozen {
17+
Err(ProgramError::Custom(7))
18+
} else {
19+
Ok(())
20+
}
21+
}
22+
23+
#[inline(never)]
24+
fn cheatcode_is_spl_account(_: &AccountInfo<'_>) {}
25+
26+
#[derive(Clone)]
27+
struct AccountInfo<'a> {
28+
key: &'a Pubkey,
29+
lamports: Rc<RefCell<&'a mut u64>>,
30+
data: Rc<RefCell<&'a mut [u8]>>,
31+
owner: &'a Pubkey,
32+
rent_epoch: u64,
33+
is_signer: bool,
34+
is_writable: bool,
35+
executable: bool,
36+
}
37+
38+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
39+
struct Pubkey([u8; 32]);
40+
41+
impl Pubkey {
42+
fn new(bytes: [u8; 32]) -> Self {
43+
Self(bytes)
44+
}
45+
}
46+
47+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48+
enum ProgramError {
49+
InvalidAccountData,
50+
Custom(u32),
51+
}
52+
53+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
54+
enum AccountState {
55+
Uninitialized,
56+
Initialized,
57+
Frozen,
58+
}
59+
60+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61+
enum COption<T> {
62+
None,
63+
Some(T),
64+
}
65+
66+
#[derive(Clone, Copy, Debug)]
67+
struct Account {
68+
mint: Pubkey,
69+
owner: Pubkey,
70+
amount: u64,
71+
delegate: COption<Pubkey>,
72+
state: AccountState,
73+
is_native: COption<u64>,
74+
delegated_amount: u64,
75+
close_authority: COption<Pubkey>,
76+
}
77+
78+
impl Account {
79+
const LEN: usize = 165;
80+
81+
fn unpack_from_slice(data: &[u8]) -> Result<Self, ProgramError> {
82+
if data.len() < Self::LEN {
83+
return Err(ProgramError::InvalidAccountData);
84+
}
85+
86+
let mint = Pubkey::new(data[0..32].try_into().unwrap());
87+
let owner = Pubkey::new(data[32..64].try_into().unwrap());
88+
let amount = u64::from_le_bytes(data[64..72].try_into().unwrap());
89+
let state = match data[108] {
90+
0 => AccountState::Uninitialized,
91+
1 => AccountState::Initialized,
92+
2 => AccountState::Frozen,
93+
_ => return Err(ProgramError::InvalidAccountData),
94+
};
95+
96+
Ok(Self {
97+
mint,
98+
owner,
99+
amount,
100+
delegate: COption::None,
101+
state,
102+
is_native: COption::None,
103+
delegated_amount: 0,
104+
close_authority: COption::None,
105+
})
106+
}
107+
108+
fn unpack_unchecked(data: &[u8]) -> Result<Self, ProgramError> {
109+
Self::unpack_from_slice(data)
110+
}
111+
}
112+
113+
#[inline(never)]
114+
fn get_account(acc: &AccountInfo<'_>) -> Result<Account, ProgramError> {
115+
Account::unpack_unchecked(&acc.data.borrow())
116+
}

kmir/src/tests/integration/test_integration.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
'transmute-bytes': ['bytes_to_u64', 'u64_to_bytes'],
4040
'test_offset_from-fail': ['testing'],
4141
'iter-eq-copied-take-dereftruncate': ['repro'],
42+
'spl-accountstate-discriminant-branch': ['repro'],
4243
'spl-multisig-iter-eq-copied-next-fail': ['repro'],
4344
'spl-multisig-signer-index': ['repro'],
4445
}

0 commit comments

Comments
 (0)