Skip to content

Commit 4b89b0f

Browse files
authored
Check account boundaries on overlapping memmove syscall (#4563)
* fix reverse memmove
1 parent 1ea7620 commit 4b89b0f

File tree

4 files changed

+59
-7
lines changed

4 files changed

+59
-7
lines changed

programs/bpf_loader/src/syscalls/mem_ops.rs

+43-4
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ struct MemoryChunkIterator<'a> {
419419
// exclusive end index (start + len, so one past the last valid address)
420420
vm_addr_end: u64,
421421
len: u64,
422-
account_index: usize,
422+
account_index: Option<usize>,
423423
is_account: Option<bool>,
424424
}
425425

@@ -446,7 +446,7 @@ impl<'a> MemoryChunkIterator<'a> {
446446
len,
447447
vm_addr_start: vm_addr,
448448
vm_addr_end,
449-
account_index: 0,
449+
account_index: None,
450450
is_account: None,
451451
})
452452
}
@@ -490,14 +490,18 @@ impl<'a> Iterator for MemoryChunkIterator<'a> {
490490

491491
let region_is_account;
492492

493+
let mut account_index = self.account_index.unwrap_or_default();
494+
self.account_index = Some(account_index);
495+
493496
loop {
494-
if let Some(account) = self.accounts.get(self.account_index) {
497+
if let Some(account) = self.accounts.get(account_index) {
495498
let account_addr = account.vm_data_addr;
496499
let resize_addr = account_addr.saturating_add(account.original_data_len as u64);
497500

498501
if resize_addr < region.vm_addr {
499502
// region is after this account, move on next one
500-
self.account_index = self.account_index.saturating_add(1);
503+
account_index = account_index.saturating_add(1);
504+
self.account_index = Some(account_index);
501505
} else {
502506
region_is_account =
503507
region.vm_addr == account_addr || region.vm_addr == resize_addr;
@@ -550,6 +554,41 @@ impl DoubleEndedIterator for MemoryChunkIterator<'_> {
550554
}
551555
};
552556

557+
let region_is_account;
558+
559+
let mut account_index = self
560+
.account_index
561+
.unwrap_or_else(|| self.accounts.len().saturating_sub(1));
562+
self.account_index = Some(account_index);
563+
564+
loop {
565+
let Some(account) = self.accounts.get(account_index) else {
566+
// address is after all the accounts
567+
region_is_account = false;
568+
break;
569+
};
570+
571+
let account_addr = account.vm_data_addr;
572+
let resize_addr = account_addr.saturating_add(account.original_data_len as u64);
573+
574+
if account_index > 0 && account_addr > region.vm_addr {
575+
account_index = account_index.saturating_sub(1);
576+
577+
self.account_index = Some(account_index);
578+
} else {
579+
region_is_account = region.vm_addr == account_addr || region.vm_addr == resize_addr;
580+
break;
581+
}
582+
}
583+
584+
if let Some(is_account) = self.is_account {
585+
if is_account != region_is_account {
586+
return Some(Err(SyscallError::InvalidLength.into()));
587+
}
588+
} else {
589+
self.is_account = Some(region_is_account);
590+
}
591+
553592
let chunk_len = if region.vm_addr >= self.vm_addr_start {
554593
// consume the whole region
555594
let len = self.vm_addr_end.saturating_sub(region.vm_addr);

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

+14-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,20 @@ pub fn process_instruction(
108108
// memmov dst overlaps begin of account
109109
unsafe { sol_memmove(too_early(3).as_mut_ptr(), buf.as_ptr(), 10) };
110110
}
111-
111+
14 => {
112+
// memmove dst overlaps begin of account, reverse order
113+
unsafe { sol_memmove(too_early(0).as_mut_ptr(), too_early(3).as_ptr(), 10) };
114+
}
115+
15 => {
116+
// memmove dst overlaps end of account, reverse order
117+
unsafe {
118+
sol_memmove(
119+
data[data_len..].as_mut_ptr(),
120+
data[data_len.saturating_sub(3)..].as_mut_ptr(),
121+
10,
122+
)
123+
};
124+
}
112125
_ => {}
113126
}
114127

programs/sbf/tests/programs.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5644,7 +5644,7 @@ fn test_mem_syscalls_overlap_account_begin_or_end() {
56445644
let account = AccountSharedData::new(42, 1024, &program_id);
56455645
bank.store_account(&account_keypair.pubkey(), &account);
56465646

5647-
for instr in 0..=13 {
5647+
for instr in 0..=15 {
56485648
println!("Testing direct_mapping:{direct_mapping} instruction:{instr}");
56495649
let instruction =
56505650
Instruction::new_with_bytes(program_id, &[instr], account_metas.clone());

sdk/feature-set/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ pub mod apply_cost_tracker_during_replay {
617617
solana_pubkey::declare_id!("2ry7ygxiYURULZCrypHhveanvP5tzZ4toRwVp89oCNSj");
618618
}
619619
pub mod bpf_account_data_direct_mapping {
620-
solana_pubkey::declare_id!("GJVDwRkUPNdk9QaK4VsU4g1N41QNxhy1hevjf8kz45Mq");
620+
solana_pubkey::declare_id!("FNPWmNbHbYy1R8JWVZgCPqsoRBcRu4F6ezSnq5o97Px");
621621
}
622622

623623
pub mod add_set_tx_loaded_accounts_data_size_instruction {

0 commit comments

Comments
 (0)