Skip to content

Commit 32b445a

Browse files
v2.1: Ensure that Rc in AccountInfo is not stored in an account (backport of #3471) (#3723)
Ensure that Rc in AccountInfo is not stored in an account (#3471) * Ensure that Rc in AccountInfo is not stored in an account (cherry picked from commit 030a558) Co-authored-by: Sean Young <[email protected]>
1 parent 086cc09 commit 32b445a

File tree

4 files changed

+174
-0
lines changed

4 files changed

+174
-0
lines changed

programs/bpf_loader/src/syscalls/cpi.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ impl<'a, 'b> CallerAccount<'a, 'b> {
137137
invoke_context.get_check_aligned(),
138138
)?;
139139
if direct_mapping {
140+
if account_info.lamports.as_ptr() as u64 >= ebpf::MM_INPUT_START {
141+
return Err(SyscallError::InvalidPointer.into());
142+
}
143+
140144
check_account_info_pointer(
141145
invoke_context,
142146
*ptr,
@@ -154,6 +158,10 @@ impl<'a, 'b> CallerAccount<'a, 'b> {
154158
)?;
155159

156160
let (serialized_data, vm_data_addr, ref_to_len_in_vm) = {
161+
if direct_mapping && account_info.data.as_ptr() as u64 >= ebpf::MM_INPUT_START {
162+
return Err(SyscallError::InvalidPointer.into());
163+
}
164+
157165
// Double translate data out of RefCell
158166
let data = *translate_type::<&[u8]>(
159167
memory_mapping,

programs/sbf/rust/invoke/src/lib.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,6 +1522,78 @@ fn process_instruction<'a>(
15221522
)
15231523
.unwrap();
15241524
}
1525+
TEST_ACCOUNT_INFO_LAMPORTS_RC => {
1526+
msg!("TEST_ACCOUNT_INFO_LAMPORTS_RC_IN_ACCOUNT");
1527+
1528+
let mut account0 = accounts[0].clone();
1529+
let account1 = accounts[1].clone();
1530+
let account2 = accounts[2].clone();
1531+
1532+
account0.lamports = unsafe {
1533+
let dst = account1.data.borrow_mut().as_mut_ptr();
1534+
// 32 = size_of::<RcBox>()
1535+
std::ptr::copy(
1536+
std::mem::transmute::<Rc<RefCell<&mut u64>>, *const u8>(account0.lamports),
1537+
dst,
1538+
32,
1539+
);
1540+
std::mem::transmute::<*mut u8, Rc<RefCell<&mut u64>>>(dst)
1541+
};
1542+
1543+
let mut instruction_data = vec![TEST_WRITE_ACCOUNT, 1];
1544+
instruction_data.extend_from_slice(&1u64.to_le_bytes());
1545+
instruction_data.push(1);
1546+
1547+
invoke(
1548+
&create_instruction(
1549+
*program_id,
1550+
&[
1551+
(program_id, false, false),
1552+
(accounts[1].key, true, false),
1553+
(accounts[0].key, false, false),
1554+
],
1555+
instruction_data.to_vec(),
1556+
),
1557+
&[account0, account1, account2],
1558+
)
1559+
.unwrap();
1560+
}
1561+
TEST_ACCOUNT_INFO_DATA_RC => {
1562+
msg!("TEST_ACCOUNT_INFO_DATA_RC_IN_ACCOUNT");
1563+
1564+
let mut account0 = accounts[0].clone();
1565+
let account1 = accounts[1].clone();
1566+
let account2 = accounts[2].clone();
1567+
1568+
account0.data = unsafe {
1569+
let dst = account1.data.borrow_mut().as_mut_ptr();
1570+
// 32 = size_of::<RcBox>()
1571+
std::ptr::copy(
1572+
std::mem::transmute::<Rc<RefCell<&mut [u8]>>, *const u8>(account0.data),
1573+
dst,
1574+
32,
1575+
);
1576+
std::mem::transmute::<*mut u8, Rc<RefCell<&mut [u8]>>>(dst)
1577+
};
1578+
1579+
let mut instruction_data = vec![TEST_WRITE_ACCOUNT, 1];
1580+
instruction_data.extend_from_slice(&1u64.to_le_bytes());
1581+
instruction_data.push(1);
1582+
1583+
invoke(
1584+
&create_instruction(
1585+
*program_id,
1586+
&[
1587+
(program_id, false, false),
1588+
(accounts[1].key, true, false),
1589+
(accounts[0].key, false, false),
1590+
],
1591+
instruction_data.to_vec(),
1592+
),
1593+
&[account0, account1, account2],
1594+
)
1595+
.unwrap();
1596+
}
15251597
_ => panic!("unexpected program data"),
15261598
}
15271599

programs/sbf/rust/invoke_dep/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ pub const TEST_WRITE_ACCOUNT: u8 = 40;
4343
pub const TEST_CALLEE_ACCOUNT_UPDATES: u8 = 41;
4444
pub const TEST_STACK_HEAP_ZEROED: u8 = 42;
4545
pub const TEST_ACCOUNT_INFO_IN_ACCOUNT: u8 = 43;
46+
pub const TEST_ACCOUNT_INFO_LAMPORTS_RC: u8 = 44;
47+
pub const TEST_ACCOUNT_INFO_DATA_RC: u8 = 45;
4648

4749
pub const MINT_INDEX: usize = 0;
4850
pub const ARGUMENT_INDEX: usize = 1;

programs/sbf/tests/programs.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5001,6 +5001,98 @@ fn test_account_info_in_account() {
50015001
}
50025002
}
50035003

5004+
#[test]
5005+
fn test_account_info_rc_in_account() {
5006+
solana_logger::setup();
5007+
5008+
let GenesisConfigInfo {
5009+
genesis_config,
5010+
mint_keypair,
5011+
..
5012+
} = create_genesis_config(100_123_456_789);
5013+
5014+
for direct_mapping in [false, true] {
5015+
let mut bank = Bank::new_for_tests(&genesis_config);
5016+
let feature_set = Arc::make_mut(&mut bank.feature_set);
5017+
// by default test banks have all features enabled, so we only need to
5018+
// disable when needed
5019+
if !direct_mapping {
5020+
feature_set.deactivate(&feature_set::bpf_account_data_direct_mapping::id());
5021+
}
5022+
5023+
let (bank, bank_forks) = bank.wrap_with_bank_forks_for_tests();
5024+
let mut bank_client = BankClient::new_shared(bank.clone());
5025+
let authority_keypair = Keypair::new();
5026+
5027+
let (bank, invoke_program_id) = load_upgradeable_program_and_advance_slot(
5028+
&mut bank_client,
5029+
bank_forks.as_ref(),
5030+
&mint_keypair,
5031+
&authority_keypair,
5032+
"solana_sbf_rust_invoke",
5033+
);
5034+
5035+
let account_keypair = Keypair::new();
5036+
5037+
let mint_pubkey = mint_keypair.pubkey();
5038+
5039+
let account_metas = vec![
5040+
AccountMeta::new(mint_pubkey, true),
5041+
AccountMeta::new(account_keypair.pubkey(), false),
5042+
AccountMeta::new_readonly(invoke_program_id, false),
5043+
];
5044+
5045+
let instruction_data = vec![TEST_ACCOUNT_INFO_LAMPORTS_RC, 0, 0, 0];
5046+
5047+
let instruction = Instruction::new_with_bytes(
5048+
invoke_program_id,
5049+
&instruction_data,
5050+
account_metas.clone(),
5051+
);
5052+
5053+
let account = AccountSharedData::new(42, 10240, &invoke_program_id);
5054+
5055+
bank.store_account(&account_keypair.pubkey(), &account);
5056+
5057+
let message = Message::new(&[instruction], Some(&mint_pubkey));
5058+
let tx = Transaction::new(&[&mint_keypair], message.clone(), bank.last_blockhash());
5059+
let (result, _, logs) = process_transaction_and_record_inner(&bank, tx);
5060+
5061+
if direct_mapping {
5062+
assert!(
5063+
logs.last().unwrap().ends_with(" failed: Invalid pointer"),
5064+
"{logs:?}"
5065+
);
5066+
assert!(result.is_err());
5067+
} else {
5068+
assert!(result.is_ok(), "{logs:?}");
5069+
}
5070+
5071+
let instruction_data = vec![TEST_ACCOUNT_INFO_DATA_RC, 0, 0, 0];
5072+
5073+
let instruction =
5074+
Instruction::new_with_bytes(invoke_program_id, &instruction_data, account_metas);
5075+
5076+
let account = AccountSharedData::new(42, 10240, &invoke_program_id);
5077+
5078+
bank.store_account(&account_keypair.pubkey(), &account);
5079+
5080+
let message = Message::new(&[instruction], Some(&mint_pubkey));
5081+
let tx = Transaction::new(&[&mint_keypair], message.clone(), bank.last_blockhash());
5082+
let (result, _, logs) = process_transaction_and_record_inner(&bank, tx);
5083+
5084+
if direct_mapping {
5085+
assert!(
5086+
logs.last().unwrap().ends_with(" failed: Invalid pointer"),
5087+
"{logs:?}"
5088+
);
5089+
assert!(result.is_err());
5090+
} else {
5091+
assert!(result.is_ok(), "{logs:?}");
5092+
}
5093+
}
5094+
}
5095+
50045096
#[test]
50055097
fn test_clone_account_data() {
50065098
// Test cloning account data works as expect with

0 commit comments

Comments
 (0)