1use {
2 super::*,
3 crate::{translate_inner, translate_slice_inner, translate_type_inner},
4 solana_instruction::Instruction,
5 solana_loader_v3_interface::instruction as bpf_loader_upgradeable,
6 solana_program_runtime::{
7 invoke_context::SerializedAccountMetadata,
8 serialization::{create_memory_region_of_account, modify_memory_region_of_account},
9 },
10 solana_sbpf::ebpf,
11 solana_stable_layout::stable_instruction::StableInstruction,
12 solana_svm_measure::measure::Measure,
13 solana_transaction_context::{
14 BorrowedAccount, MAX_ACCOUNTS_PER_INSTRUCTION, MAX_INSTRUCTION_DATA_LEN,
15 },
16 std::mem,
17};
18
19const MAX_CPI_ACCOUNT_INFOS: usize = 128;
20
21fn check_account_info_pointer(
22 invoke_context: &InvokeContext,
23 vm_addr: u64,
24 expected_vm_addr: u64,
25 field: &str,
26) -> Result<(), Error> {
27 if vm_addr != expected_vm_addr {
28 ic_msg!(
29 invoke_context,
30 "Invalid account info pointer `{}': {:#x} != {:#x}",
31 field,
32 vm_addr,
33 expected_vm_addr
34 );
35 return Err(SyscallError::InvalidPointer.into());
36 }
37 Ok(())
38}
39
40fn translate_type_mut<'a, T>(
42 memory_mapping: &MemoryMapping,
43 vm_addr: u64,
44 check_aligned: bool,
45) -> Result<&'a mut T, Error> {
46 translate_type_inner!(memory_mapping, AccessType::Store, vm_addr, T, check_aligned)
47}
48fn translate_slice_mut<'a, T>(
50 memory_mapping: &MemoryMapping,
51 vm_addr: u64,
52 len: u64,
53 check_aligned: bool,
54) -> Result<&'a mut [T], Error> {
55 translate_slice_inner!(
56 memory_mapping,
57 AccessType::Store,
58 vm_addr,
59 len,
60 T,
61 check_aligned,
62 )
63}
64
65struct CallerAccount<'a> {
70 lamports: &'a mut u64,
71 owner: &'a mut Pubkey,
72 original_data_len: usize,
77 serialized_data: &'a mut [u8],
83 vm_data_addr: u64,
86 ref_to_len_in_vm: &'a mut u64,
87}
88
89impl<'a> CallerAccount<'a> {
90 fn get_serialized_data(
91 memory_mapping: &MemoryMapping<'_>,
92 vm_addr: u64,
93 len: u64,
94 stricter_abi_and_runtime_constraints: bool,
95 account_data_direct_mapping: bool,
96 ) -> Result<&'a mut [u8], Error> {
97 if stricter_abi_and_runtime_constraints && account_data_direct_mapping {
98 Ok(&mut [])
99 } else if stricter_abi_and_runtime_constraints {
100 let serialization_ptr = translate_slice_mut::<u8>(
102 memory_mapping,
103 solana_sbpf::ebpf::MM_INPUT_START,
104 1,
105 false, )?
107 .as_mut_ptr();
108 unsafe {
109 Ok(std::slice::from_raw_parts_mut(
110 serialization_ptr
111 .add(vm_addr.saturating_sub(solana_sbpf::ebpf::MM_INPUT_START) as usize),
112 len as usize,
113 ))
114 }
115 } else {
116 translate_slice_mut::<u8>(
117 memory_mapping,
118 vm_addr,
119 len,
120 false, )
122 }
123 }
124
125 fn from_account_info(
127 invoke_context: &InvokeContext,
128 memory_mapping: &MemoryMapping<'_>,
129 check_aligned: bool,
130 _vm_addr: u64,
131 account_info: &AccountInfo,
132 account_metadata: &SerializedAccountMetadata,
133 ) -> Result<CallerAccount<'a>, Error> {
134 let stricter_abi_and_runtime_constraints = invoke_context
135 .get_feature_set()
136 .stricter_abi_and_runtime_constraints;
137
138 if stricter_abi_and_runtime_constraints {
139 check_account_info_pointer(
140 invoke_context,
141 account_info.key as *const _ as u64,
142 account_metadata.vm_key_addr,
143 "key",
144 )?;
145 check_account_info_pointer(
146 invoke_context,
147 account_info.owner as *const _ as u64,
148 account_metadata.vm_owner_addr,
149 "owner",
150 )?;
151 }
152
153 let lamports = {
156 let ptr = translate_type::<u64>(
158 memory_mapping,
159 account_info.lamports.as_ptr() as u64,
160 check_aligned,
161 )?;
162 if stricter_abi_and_runtime_constraints {
163 if account_info.lamports.as_ptr() as u64 >= ebpf::MM_INPUT_START {
164 return Err(SyscallError::InvalidPointer.into());
165 }
166
167 check_account_info_pointer(
168 invoke_context,
169 *ptr,
170 account_metadata.vm_lamports_addr,
171 "lamports",
172 )?;
173 }
174 translate_type_mut::<u64>(memory_mapping, *ptr, check_aligned)?
175 };
176
177 let owner = translate_type_mut::<Pubkey>(
178 memory_mapping,
179 account_info.owner as *const _ as u64,
180 check_aligned,
181 )?;
182
183 let (serialized_data, vm_data_addr, ref_to_len_in_vm) = {
184 if stricter_abi_and_runtime_constraints
185 && account_info.data.as_ptr() as u64 >= ebpf::MM_INPUT_START
186 {
187 return Err(SyscallError::InvalidPointer.into());
188 }
189
190 let data = *translate_type::<&[u8]>(
192 memory_mapping,
193 account_info.data.as_ptr() as *const _ as u64,
194 check_aligned,
195 )?;
196 if stricter_abi_and_runtime_constraints {
197 check_account_info_pointer(
198 invoke_context,
199 data.as_ptr() as u64,
200 account_metadata.vm_data_addr,
201 "data",
202 )?;
203 }
204
205 consume_compute_meter(
206 invoke_context,
207 (data.len() as u64)
208 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
209 .unwrap_or(u64::MAX),
210 )?;
211
212 let vm_len_addr = (account_info.data.as_ptr() as *const u64 as u64)
213 .saturating_add(size_of::<u64>() as u64);
214 if stricter_abi_and_runtime_constraints {
215 if vm_len_addr >= ebpf::MM_INPUT_START {
219 return Err(SyscallError::InvalidPointer.into());
220 }
221 }
222 let ref_to_len_in_vm = translate_type_mut::<u64>(memory_mapping, vm_len_addr, false)?;
223 let vm_data_addr = data.as_ptr() as u64;
224 let serialized_data = CallerAccount::get_serialized_data(
225 memory_mapping,
226 vm_data_addr,
227 data.len() as u64,
228 stricter_abi_and_runtime_constraints,
229 invoke_context.account_data_direct_mapping,
230 )?;
231 (serialized_data, vm_data_addr, ref_to_len_in_vm)
232 };
233
234 Ok(CallerAccount {
235 lamports,
236 owner,
237 original_data_len: account_metadata.original_data_len,
238 serialized_data,
239 vm_data_addr,
240 ref_to_len_in_vm,
241 })
242 }
243
244 fn from_sol_account_info(
246 invoke_context: &InvokeContext,
247 memory_mapping: &MemoryMapping<'_>,
248 check_aligned: bool,
249 vm_addr: u64,
250 account_info: &SolAccountInfo,
251 account_metadata: &SerializedAccountMetadata,
252 ) -> Result<CallerAccount<'a>, Error> {
253 let stricter_abi_and_runtime_constraints = invoke_context
254 .get_feature_set()
255 .stricter_abi_and_runtime_constraints;
256
257 if stricter_abi_and_runtime_constraints {
258 check_account_info_pointer(
259 invoke_context,
260 account_info.key_addr,
261 account_metadata.vm_key_addr,
262 "key",
263 )?;
264
265 check_account_info_pointer(
266 invoke_context,
267 account_info.owner_addr,
268 account_metadata.vm_owner_addr,
269 "owner",
270 )?;
271
272 check_account_info_pointer(
273 invoke_context,
274 account_info.lamports_addr,
275 account_metadata.vm_lamports_addr,
276 "lamports",
277 )?;
278
279 check_account_info_pointer(
280 invoke_context,
281 account_info.data_addr,
282 account_metadata.vm_data_addr,
283 "data",
284 )?;
285 }
286
287 let lamports =
290 translate_type_mut::<u64>(memory_mapping, account_info.lamports_addr, check_aligned)?;
291 let owner =
292 translate_type_mut::<Pubkey>(memory_mapping, account_info.owner_addr, check_aligned)?;
293
294 consume_compute_meter(
295 invoke_context,
296 account_info
297 .data_len
298 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
299 .unwrap_or(u64::MAX),
300 )?;
301
302 let serialized_data = CallerAccount::get_serialized_data(
303 memory_mapping,
304 account_info.data_addr,
305 account_info.data_len,
306 stricter_abi_and_runtime_constraints,
307 invoke_context.account_data_direct_mapping,
308 )?;
309
310 let vm_len_addr = vm_addr
315 .saturating_add(&account_info.data_len as *const u64 as u64)
316 .saturating_sub(account_info as *const _ as *const u64 as u64);
317 let ref_to_len_in_vm = translate_type_mut::<u64>(memory_mapping, vm_len_addr, false)?;
318
319 Ok(CallerAccount {
320 lamports,
321 owner,
322 original_data_len: account_metadata.original_data_len,
323 serialized_data,
324 vm_data_addr: account_info.data_addr,
325 ref_to_len_in_vm,
326 })
327 }
328}
329
330struct TranslatedAccount<'a> {
331 index_in_caller: IndexOfAccount,
332 caller_account: CallerAccount<'a>,
333 update_caller_account_region: bool,
334 update_caller_account_info: bool,
335}
336
337trait SyscallInvokeSigned {
339 fn translate_instruction(
340 addr: u64,
341 memory_mapping: &MemoryMapping,
342 invoke_context: &mut InvokeContext,
343 check_aligned: bool,
344 ) -> Result<Instruction, Error>;
345 fn translate_accounts<'a>(
346 account_infos_addr: u64,
347 account_infos_len: u64,
348 memory_mapping: &MemoryMapping<'_>,
349 invoke_context: &mut InvokeContext,
350 check_aligned: bool,
351 ) -> Result<Vec<TranslatedAccount<'a>>, Error>;
352 fn translate_signers(
353 program_id: &Pubkey,
354 signers_seeds_addr: u64,
355 signers_seeds_len: u64,
356 memory_mapping: &MemoryMapping,
357 check_aligned: bool,
358 ) -> Result<Vec<Pubkey>, Error>;
359}
360
361declare_builtin_function!(
362 SyscallInvokeSignedRust,
364 fn rust(
365 invoke_context: &mut InvokeContext,
366 instruction_addr: u64,
367 account_infos_addr: u64,
368 account_infos_len: u64,
369 signers_seeds_addr: u64,
370 signers_seeds_len: u64,
371 memory_mapping: &mut MemoryMapping,
372 ) -> Result<u64, Error> {
373 cpi_common::<Self>(
374 invoke_context,
375 instruction_addr,
376 account_infos_addr,
377 account_infos_len,
378 signers_seeds_addr,
379 signers_seeds_len,
380 memory_mapping,
381 )
382 }
383);
384
385impl SyscallInvokeSigned for SyscallInvokeSignedRust {
386 fn translate_instruction(
387 addr: u64,
388 memory_mapping: &MemoryMapping,
389 invoke_context: &mut InvokeContext,
390 check_aligned: bool,
391 ) -> Result<Instruction, Error> {
392 let ix = translate_type::<StableInstruction>(memory_mapping, addr, check_aligned)?;
393 let account_metas = translate_slice::<AccountMeta>(
394 memory_mapping,
395 ix.accounts.as_vaddr(),
396 ix.accounts.len(),
397 check_aligned,
398 )?;
399 let data = translate_slice::<u8>(
400 memory_mapping,
401 ix.data.as_vaddr(),
402 ix.data.len(),
403 check_aligned,
404 )?
405 .to_vec();
406
407 check_instruction_size(account_metas.len(), data.len())?;
408
409 consume_compute_meter(
410 invoke_context,
411 (data.len() as u64)
412 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
413 .unwrap_or(u64::MAX),
414 )?;
415
416 let mut accounts = Vec::with_capacity(account_metas.len());
417 #[allow(clippy::needless_range_loop)]
418 for account_index in 0..account_metas.len() {
419 #[allow(clippy::indexing_slicing)]
420 let account_meta = &account_metas[account_index];
421 if unsafe {
422 std::ptr::read_volatile(&account_meta.is_signer as *const _ as *const u8) > 1
423 || std::ptr::read_volatile(&account_meta.is_writable as *const _ as *const u8)
424 > 1
425 } {
426 return Err(Box::new(InstructionError::InvalidArgument));
427 }
428 accounts.push(account_meta.clone());
429 }
430
431 Ok(Instruction {
432 accounts,
433 data,
434 program_id: ix.program_id,
435 })
436 }
437
438 fn translate_accounts<'a>(
439 account_infos_addr: u64,
440 account_infos_len: u64,
441 memory_mapping: &MemoryMapping<'_>,
442 invoke_context: &mut InvokeContext,
443 check_aligned: bool,
444 ) -> Result<Vec<TranslatedAccount<'a>>, Error> {
445 let (account_infos, account_info_keys) = translate_account_infos(
446 account_infos_addr,
447 account_infos_len,
448 |account_info: &AccountInfo| account_info.key as *const _ as u64,
449 memory_mapping,
450 invoke_context,
451 check_aligned,
452 )?;
453
454 translate_and_update_accounts(
455 &account_info_keys,
456 account_infos,
457 account_infos_addr,
458 invoke_context,
459 memory_mapping,
460 check_aligned,
461 CallerAccount::from_account_info,
462 )
463 }
464
465 fn translate_signers(
466 program_id: &Pubkey,
467 signers_seeds_addr: u64,
468 signers_seeds_len: u64,
469 memory_mapping: &MemoryMapping,
470 check_aligned: bool,
471 ) -> Result<Vec<Pubkey>, Error> {
472 let mut signers = Vec::new();
473 if signers_seeds_len > 0 {
474 let signers_seeds = translate_slice::<VmSlice<VmSlice<u8>>>(
475 memory_mapping,
476 signers_seeds_addr,
477 signers_seeds_len,
478 check_aligned,
479 )?;
480 if signers_seeds.len() > MAX_SIGNERS {
481 return Err(Box::new(SyscallError::TooManySigners));
482 }
483 for signer_seeds in signers_seeds.iter() {
484 let untranslated_seeds = translate_slice::<VmSlice<u8>>(
485 memory_mapping,
486 signer_seeds.ptr(),
487 signer_seeds.len(),
488 check_aligned,
489 )?;
490 if untranslated_seeds.len() > MAX_SEEDS {
491 return Err(Box::new(InstructionError::MaxSeedLengthExceeded));
492 }
493 let seeds = untranslated_seeds
494 .iter()
495 .map(|untranslated_seed| {
496 untranslated_seed.translate(memory_mapping, check_aligned)
497 })
498 .collect::<Result<Vec<_>, Error>>()?;
499 let signer = Pubkey::create_program_address(&seeds, program_id)
500 .map_err(SyscallError::BadSeeds)?;
501 signers.push(signer);
502 }
503 Ok(signers)
504 } else {
505 Ok(vec![])
506 }
507 }
508}
509
510#[derive(Debug)]
512#[repr(C)]
513struct SolInstruction {
514 program_id_addr: u64,
515 accounts_addr: u64,
516 accounts_len: u64,
517 data_addr: u64,
518 data_len: u64,
519}
520
521#[derive(Debug)]
523#[repr(C)]
524struct SolAccountMeta {
525 pubkey_addr: u64,
526 is_writable: bool,
527 is_signer: bool,
528}
529
530#[derive(Debug)]
532#[repr(C)]
533struct SolAccountInfo {
534 key_addr: u64,
535 lamports_addr: u64,
536 data_len: u64,
537 data_addr: u64,
538 owner_addr: u64,
539 rent_epoch: u64,
540 is_signer: bool,
541 is_writable: bool,
542 executable: bool,
543}
544
545#[derive(Debug)]
547#[repr(C)]
548struct SolSignerSeedC {
549 addr: u64,
550 len: u64,
551}
552
553#[derive(Debug)]
555#[repr(C)]
556struct SolSignerSeedsC {
557 addr: u64,
558 len: u64,
559}
560
561declare_builtin_function!(
562 SyscallInvokeSignedC,
564 fn rust(
565 invoke_context: &mut InvokeContext,
566 instruction_addr: u64,
567 account_infos_addr: u64,
568 account_infos_len: u64,
569 signers_seeds_addr: u64,
570 signers_seeds_len: u64,
571 memory_mapping: &mut MemoryMapping,
572 ) -> Result<u64, Error> {
573 cpi_common::<Self>(
574 invoke_context,
575 instruction_addr,
576 account_infos_addr,
577 account_infos_len,
578 signers_seeds_addr,
579 signers_seeds_len,
580 memory_mapping,
581 )
582 }
583);
584
585impl SyscallInvokeSigned for SyscallInvokeSignedC {
586 fn translate_instruction(
587 addr: u64,
588 memory_mapping: &MemoryMapping,
589 invoke_context: &mut InvokeContext,
590 check_aligned: bool,
591 ) -> Result<Instruction, Error> {
592 let ix_c = translate_type::<SolInstruction>(memory_mapping, addr, check_aligned)?;
593
594 let program_id =
595 translate_type::<Pubkey>(memory_mapping, ix_c.program_id_addr, check_aligned)?;
596 let account_metas = translate_slice::<SolAccountMeta>(
597 memory_mapping,
598 ix_c.accounts_addr,
599 ix_c.accounts_len,
600 check_aligned,
601 )?;
602 let data =
603 translate_slice::<u8>(memory_mapping, ix_c.data_addr, ix_c.data_len, check_aligned)?
604 .to_vec();
605
606 check_instruction_size(ix_c.accounts_len as usize, data.len())?;
607
608 consume_compute_meter(
609 invoke_context,
610 (data.len() as u64)
611 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
612 .unwrap_or(u64::MAX),
613 )?;
614
615 let mut accounts = Vec::with_capacity(ix_c.accounts_len as usize);
616 #[allow(clippy::needless_range_loop)]
617 for account_index in 0..ix_c.accounts_len as usize {
618 #[allow(clippy::indexing_slicing)]
619 let account_meta = &account_metas[account_index];
620 if unsafe {
621 std::ptr::read_volatile(&account_meta.is_signer as *const _ as *const u8) > 1
622 || std::ptr::read_volatile(&account_meta.is_writable as *const _ as *const u8)
623 > 1
624 } {
625 return Err(Box::new(InstructionError::InvalidArgument));
626 }
627 let pubkey =
628 translate_type::<Pubkey>(memory_mapping, account_meta.pubkey_addr, check_aligned)?;
629 accounts.push(AccountMeta {
630 pubkey: *pubkey,
631 is_signer: account_meta.is_signer,
632 is_writable: account_meta.is_writable,
633 });
634 }
635
636 Ok(Instruction {
637 accounts,
638 data,
639 program_id: *program_id,
640 })
641 }
642
643 fn translate_accounts<'a>(
644 account_infos_addr: u64,
645 account_infos_len: u64,
646 memory_mapping: &MemoryMapping<'_>,
647 invoke_context: &mut InvokeContext,
648 check_aligned: bool,
649 ) -> Result<Vec<TranslatedAccount<'a>>, Error> {
650 let (account_infos, account_info_keys) = translate_account_infos(
651 account_infos_addr,
652 account_infos_len,
653 |account_info: &SolAccountInfo| account_info.key_addr,
654 memory_mapping,
655 invoke_context,
656 check_aligned,
657 )?;
658
659 translate_and_update_accounts(
660 &account_info_keys,
661 account_infos,
662 account_infos_addr,
663 invoke_context,
664 memory_mapping,
665 check_aligned,
666 CallerAccount::from_sol_account_info,
667 )
668 }
669
670 fn translate_signers(
671 program_id: &Pubkey,
672 signers_seeds_addr: u64,
673 signers_seeds_len: u64,
674 memory_mapping: &MemoryMapping,
675 check_aligned: bool,
676 ) -> Result<Vec<Pubkey>, Error> {
677 if signers_seeds_len > 0 {
678 let signers_seeds = translate_slice::<SolSignerSeedsC>(
679 memory_mapping,
680 signers_seeds_addr,
681 signers_seeds_len,
682 check_aligned,
683 )?;
684 if signers_seeds.len() > MAX_SIGNERS {
685 return Err(Box::new(SyscallError::TooManySigners));
686 }
687 Ok(signers_seeds
688 .iter()
689 .map(|signer_seeds| {
690 let seeds = translate_slice::<SolSignerSeedC>(
691 memory_mapping,
692 signer_seeds.addr,
693 signer_seeds.len,
694 check_aligned,
695 )?;
696 if seeds.len() > MAX_SEEDS {
697 return Err(Box::new(InstructionError::MaxSeedLengthExceeded) as Error);
698 }
699 let seeds_bytes = seeds
700 .iter()
701 .map(|seed| {
702 translate_slice::<u8>(
703 memory_mapping,
704 seed.addr,
705 seed.len,
706 check_aligned,
707 )
708 })
709 .collect::<Result<Vec<_>, Error>>()?;
710 Pubkey::create_program_address(&seeds_bytes, program_id)
711 .map_err(|err| Box::new(SyscallError::BadSeeds(err)) as Error)
712 })
713 .collect::<Result<Vec<_>, Error>>()?)
714 } else {
715 Ok(vec![])
716 }
717 }
718}
719
720fn translate_account_infos<'a, T, F>(
721 account_infos_addr: u64,
722 account_infos_len: u64,
723 key_addr: F,
724 memory_mapping: &'a MemoryMapping,
725 invoke_context: &mut InvokeContext,
726 check_aligned: bool,
727) -> Result<(&'a [T], Vec<&'a Pubkey>), Error>
728where
729 F: Fn(&T) -> u64,
730{
731 let stricter_abi_and_runtime_constraints = invoke_context
732 .get_feature_set()
733 .stricter_abi_and_runtime_constraints;
734
735 if stricter_abi_and_runtime_constraints
739 && account_infos_addr
740 .saturating_add(account_infos_len.saturating_mul(std::mem::size_of::<T>() as u64))
741 >= ebpf::MM_INPUT_START
742 {
743 return Err(SyscallError::InvalidPointer.into());
744 }
745
746 let account_infos = translate_slice::<T>(
747 memory_mapping,
748 account_infos_addr,
749 account_infos_len,
750 check_aligned,
751 )?;
752 check_account_infos(account_infos.len(), invoke_context)?;
753 let mut account_info_keys = Vec::with_capacity(account_infos_len as usize);
754 #[allow(clippy::needless_range_loop)]
755 for account_index in 0..account_infos_len as usize {
756 #[allow(clippy::indexing_slicing)]
757 let account_info = &account_infos[account_index];
758 account_info_keys.push(translate_type::<Pubkey>(
759 memory_mapping,
760 key_addr(account_info),
761 check_aligned,
762 )?);
763 }
764 Ok((account_infos, account_info_keys))
765}
766
767fn translate_and_update_accounts<'a, T, F>(
770 account_info_keys: &[&Pubkey],
771 account_infos: &[T],
772 account_infos_addr: u64,
773 invoke_context: &mut InvokeContext,
774 memory_mapping: &MemoryMapping<'_>,
775 check_aligned: bool,
776 do_translate: F,
777) -> Result<Vec<TranslatedAccount<'a>>, Error>
778where
779 F: Fn(
780 &InvokeContext,
781 &MemoryMapping<'_>,
782 bool,
783 u64,
784 &T,
785 &SerializedAccountMetadata,
786 ) -> Result<CallerAccount<'a>, Error>,
787{
788 let transaction_context = &invoke_context.transaction_context;
789 let next_instruction_context = transaction_context.get_next_instruction_context()?;
790 let next_instruction_accounts = next_instruction_context.instruction_accounts();
791 let instruction_context = transaction_context.get_current_instruction_context()?;
792 let mut accounts = Vec::with_capacity(next_instruction_accounts.len());
793
794 let accounts_metadata = &invoke_context
797 .get_syscall_context()
798 .unwrap()
799 .accounts_metadata;
800
801 let stricter_abi_and_runtime_constraints = invoke_context
802 .get_feature_set()
803 .stricter_abi_and_runtime_constraints;
804
805 for (instruction_account_index, instruction_account) in
806 next_instruction_accounts.iter().enumerate()
807 {
808 if next_instruction_context
809 .is_instruction_account_duplicate(instruction_account_index as IndexOfAccount)?
810 .is_some()
811 {
812 continue; }
814
815 let index_in_caller = instruction_context
816 .get_index_of_account_in_instruction(instruction_account.index_in_transaction)?;
817 let callee_account = instruction_context.try_borrow_instruction_account(index_in_caller)?;
818 let account_key = invoke_context
819 .transaction_context
820 .get_key_of_account_at_index(instruction_account.index_in_transaction)?;
821
822 #[allow(deprecated)]
823 if callee_account.is_executable() {
824 consume_compute_meter(
826 invoke_context,
827 (callee_account.get_data().len() as u64)
828 .checked_div(invoke_context.get_execution_cost().cpi_bytes_per_unit)
829 .unwrap_or(u64::MAX),
830 )?;
831 } else if let Some(caller_account_index) =
832 account_info_keys.iter().position(|key| *key == account_key)
833 {
834 let serialized_metadata =
835 accounts_metadata
836 .get(index_in_caller as usize)
837 .ok_or_else(|| {
838 ic_msg!(
839 invoke_context,
840 "Internal error: index mismatch for account {}",
841 account_key
842 );
843 Box::new(InstructionError::MissingAccount)
844 })?;
845
846 if caller_account_index >= account_infos.len() {
848 return Err(Box::new(SyscallError::InvalidLength));
849 }
850 #[allow(clippy::indexing_slicing)]
851 let caller_account =
852 do_translate(
853 invoke_context,
854 memory_mapping,
855 check_aligned,
856 account_infos_addr.saturating_add(
857 caller_account_index.saturating_mul(mem::size_of::<T>()) as u64,
858 ),
859 &account_infos[caller_account_index],
860 serialized_metadata,
861 )?;
862
863 let update_caller = update_callee_account(
868 check_aligned,
869 &caller_account,
870 callee_account,
871 stricter_abi_and_runtime_constraints,
872 invoke_context.account_data_direct_mapping,
873 )?;
874
875 accounts.push(TranslatedAccount {
876 index_in_caller,
877 caller_account,
878 update_caller_account_region: instruction_account.is_writable() || update_caller,
879 update_caller_account_info: instruction_account.is_writable(),
880 });
881 } else {
882 ic_msg!(
883 invoke_context,
884 "Instruction references an unknown account {}",
885 account_key
886 );
887 return Err(Box::new(InstructionError::MissingAccount));
888 }
889 }
890
891 Ok(accounts)
892}
893
894fn check_instruction_size(num_accounts: usize, data_len: usize) -> Result<(), Error> {
895 if num_accounts > MAX_ACCOUNTS_PER_INSTRUCTION {
896 return Err(Box::new(SyscallError::MaxInstructionAccountsExceeded {
897 num_accounts: num_accounts as u64,
898 max_accounts: MAX_ACCOUNTS_PER_INSTRUCTION as u64,
899 }));
900 }
901 if data_len > MAX_INSTRUCTION_DATA_LEN {
902 return Err(Box::new(SyscallError::MaxInstructionDataLenExceeded {
903 data_len: data_len as u64,
904 max_data_len: MAX_INSTRUCTION_DATA_LEN as u64,
905 }));
906 }
907 Ok(())
908}
909
910fn check_account_infos(
911 num_account_infos: usize,
912 invoke_context: &mut InvokeContext,
913) -> Result<(), Error> {
914 let max_cpi_account_infos = if invoke_context
915 .get_feature_set()
916 .increase_tx_account_lock_limit
917 {
918 MAX_CPI_ACCOUNT_INFOS
919 } else {
920 64
921 };
922 let num_account_infos = num_account_infos as u64;
923 let max_account_infos = max_cpi_account_infos as u64;
924 if num_account_infos > max_account_infos {
925 return Err(Box::new(SyscallError::MaxInstructionAccountInfosExceeded {
926 num_account_infos,
927 max_account_infos,
928 }));
929 }
930 Ok(())
931}
932
933fn check_authorized_program(
934 program_id: &Pubkey,
935 instruction_data: &[u8],
936 invoke_context: &InvokeContext,
937) -> Result<(), Error> {
938 if native_loader::check_id(program_id)
939 || bpf_loader::check_id(program_id)
940 || bpf_loader_deprecated::check_id(program_id)
941 || (solana_sdk_ids::bpf_loader_upgradeable::check_id(program_id)
942 && !(bpf_loader_upgradeable::is_upgrade_instruction(instruction_data)
943 || bpf_loader_upgradeable::is_set_authority_instruction(instruction_data)
944 || (invoke_context
945 .get_feature_set()
946 .enable_bpf_loader_set_authority_checked_ix
947 && bpf_loader_upgradeable::is_set_authority_checked_instruction(
948 instruction_data,
949 ))
950 || (invoke_context
951 .get_feature_set()
952 .enable_extend_program_checked
953 && bpf_loader_upgradeable::is_extend_program_checked_instruction(
954 instruction_data,
955 ))
956 || bpf_loader_upgradeable::is_close_instruction(instruction_data)))
957 || invoke_context.is_precompile(program_id)
958 {
959 return Err(Box::new(SyscallError::ProgramNotSupported(*program_id)));
960 }
961 Ok(())
962}
963
964fn cpi_common<S: SyscallInvokeSigned>(
966 invoke_context: &mut InvokeContext,
967 instruction_addr: u64,
968 account_infos_addr: u64,
969 account_infos_len: u64,
970 signers_seeds_addr: u64,
971 signers_seeds_len: u64,
972 memory_mapping: &mut MemoryMapping,
973) -> Result<u64, Error> {
974 let check_aligned = invoke_context.get_check_aligned();
975
976 consume_compute_meter(
981 invoke_context,
982 invoke_context.get_execution_cost().invoke_units,
983 )?;
984 if let Some(execute_time) = invoke_context.execute_time.as_mut() {
985 execute_time.stop();
986 invoke_context.timings.execute_us += execute_time.as_us();
987 }
988
989 let instruction = S::translate_instruction(
990 instruction_addr,
991 memory_mapping,
992 invoke_context,
993 check_aligned,
994 )?;
995 let transaction_context = &invoke_context.transaction_context;
996 let instruction_context = transaction_context.get_current_instruction_context()?;
997 let caller_program_id = instruction_context.get_program_key()?;
998 let signers = S::translate_signers(
999 caller_program_id,
1000 signers_seeds_addr,
1001 signers_seeds_len,
1002 memory_mapping,
1003 check_aligned,
1004 )?;
1005 check_authorized_program(&instruction.program_id, &instruction.data, invoke_context)?;
1006 invoke_context.prepare_next_instruction(&instruction, &signers)?;
1007
1008 let mut accounts = S::translate_accounts(
1009 account_infos_addr,
1010 account_infos_len,
1011 memory_mapping,
1012 invoke_context,
1013 check_aligned,
1014 )?;
1015
1016 let mut compute_units_consumed = 0;
1018 invoke_context
1019 .process_instruction(&mut compute_units_consumed, &mut ExecuteTimings::default())?;
1020
1021 let transaction_context = &invoke_context.transaction_context;
1023 let instruction_context = transaction_context.get_current_instruction_context()?;
1024
1025 let stricter_abi_and_runtime_constraints = invoke_context
1029 .get_feature_set()
1030 .stricter_abi_and_runtime_constraints;
1031
1032 for translate_account in accounts.iter_mut() {
1033 let mut callee_account = instruction_context
1034 .try_borrow_instruction_account(translate_account.index_in_caller)?;
1035 if translate_account.update_caller_account_info {
1036 update_caller_account(
1037 invoke_context,
1038 memory_mapping,
1039 check_aligned,
1040 &mut translate_account.caller_account,
1041 &mut callee_account,
1042 stricter_abi_and_runtime_constraints,
1043 )?;
1044 }
1045 }
1046
1047 if stricter_abi_and_runtime_constraints {
1048 for translate_account in accounts.iter() {
1049 let mut callee_account = instruction_context
1050 .try_borrow_instruction_account(translate_account.index_in_caller)?;
1051 if translate_account.update_caller_account_region {
1052 update_caller_account_region(
1053 memory_mapping,
1054 check_aligned,
1055 &translate_account.caller_account,
1056 &mut callee_account,
1057 invoke_context.account_data_direct_mapping,
1058 )?;
1059 }
1060 }
1061 }
1062
1063 invoke_context.execute_time = Some(Measure::start("execute"));
1064 Ok(SUCCESS)
1065}
1066
1067fn update_callee_account(
1079 check_aligned: bool,
1080 caller_account: &CallerAccount,
1081 mut callee_account: BorrowedAccount<'_>,
1082 stricter_abi_and_runtime_constraints: bool,
1083 account_data_direct_mapping: bool,
1084) -> Result<bool, Error> {
1085 let mut must_update_caller = false;
1086
1087 if callee_account.get_lamports() != *caller_account.lamports {
1088 callee_account.set_lamports(*caller_account.lamports)?;
1089 }
1090
1091 if stricter_abi_and_runtime_constraints {
1092 let prev_len = callee_account.get_data().len();
1093 let post_len = *caller_account.ref_to_len_in_vm as usize;
1094 if prev_len != post_len {
1095 let is_caller_loader_deprecated = !check_aligned;
1096 let address_space_reserved_for_account = if is_caller_loader_deprecated {
1097 caller_account.original_data_len
1098 } else {
1099 caller_account
1100 .original_data_len
1101 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1102 };
1103 if post_len > address_space_reserved_for_account {
1104 return Err(InstructionError::InvalidRealloc.into());
1105 }
1106 callee_account.set_data_length(post_len)?;
1107 must_update_caller = true;
1109 }
1110 if !account_data_direct_mapping && callee_account.can_data_be_changed().is_ok() {
1111 callee_account.set_data_from_slice(caller_account.serialized_data)?;
1112 }
1113 } else {
1114 match callee_account.can_data_be_resized(caller_account.serialized_data.len()) {
1116 Ok(()) => callee_account.set_data_from_slice(caller_account.serialized_data)?,
1117 Err(err) if callee_account.get_data() != caller_account.serialized_data => {
1118 return Err(Box::new(err));
1119 }
1120 _ => {}
1121 }
1122 }
1123
1124 if callee_account.get_owner() != caller_account.owner {
1126 callee_account.set_owner(caller_account.owner.as_ref())?;
1127 must_update_caller = true;
1129 }
1130
1131 Ok(must_update_caller)
1132}
1133
1134fn update_caller_account_region(
1135 memory_mapping: &mut MemoryMapping,
1136 check_aligned: bool,
1137 caller_account: &CallerAccount,
1138 callee_account: &mut BorrowedAccount<'_>,
1139 account_data_direct_mapping: bool,
1140) -> Result<(), Error> {
1141 let is_caller_loader_deprecated = !check_aligned;
1142 let address_space_reserved_for_account = if is_caller_loader_deprecated {
1143 caller_account.original_data_len
1144 } else {
1145 caller_account
1146 .original_data_len
1147 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1148 };
1149
1150 if address_space_reserved_for_account > 0 {
1151 let (region_index, region) = memory_mapping
1154 .find_region(caller_account.vm_data_addr)
1155 .ok_or_else(|| Box::new(InstructionError::MissingAccount))?;
1156 debug_assert_eq!(region.vm_addr, caller_account.vm_data_addr);
1158 let mut new_region;
1159 if !account_data_direct_mapping {
1160 new_region = region.clone();
1161 modify_memory_region_of_account(callee_account, &mut new_region);
1162 } else {
1163 new_region = create_memory_region_of_account(callee_account, region.vm_addr)?;
1164 }
1165 memory_mapping.replace_region(region_index, new_region)?;
1166 }
1167
1168 Ok(())
1169}
1170
1171fn update_caller_account(
1184 invoke_context: &InvokeContext,
1185 memory_mapping: &MemoryMapping<'_>,
1186 check_aligned: bool,
1187 caller_account: &mut CallerAccount<'_>,
1188 callee_account: &mut BorrowedAccount<'_>,
1189 stricter_abi_and_runtime_constraints: bool,
1190) -> Result<(), Error> {
1191 *caller_account.lamports = callee_account.get_lamports();
1192 *caller_account.owner = *callee_account.get_owner();
1193
1194 let prev_len = *caller_account.ref_to_len_in_vm as usize;
1195 let post_len = callee_account.get_data().len();
1196 let is_caller_loader_deprecated = !check_aligned;
1197 let address_space_reserved_for_account =
1198 if stricter_abi_and_runtime_constraints && is_caller_loader_deprecated {
1199 caller_account.original_data_len
1200 } else {
1201 caller_account
1202 .original_data_len
1203 .saturating_add(MAX_PERMITTED_DATA_INCREASE)
1204 };
1205
1206 if post_len > address_space_reserved_for_account
1207 && (stricter_abi_and_runtime_constraints || prev_len != post_len)
1208 {
1209 let max_increase =
1210 address_space_reserved_for_account.saturating_sub(caller_account.original_data_len);
1211 ic_msg!(
1212 invoke_context,
1213 "Account data size realloc limited to {max_increase} in inner instructions",
1214 );
1215 return Err(Box::new(InstructionError::InvalidRealloc));
1216 }
1217
1218 if prev_len != post_len {
1219 if !(stricter_abi_and_runtime_constraints && invoke_context.account_data_direct_mapping) {
1222 if post_len < prev_len {
1225 caller_account
1226 .serialized_data
1227 .get_mut(post_len..)
1228 .ok_or_else(|| Box::new(InstructionError::AccountDataTooSmall))?
1229 .fill(0);
1230 }
1231 caller_account.serialized_data = CallerAccount::get_serialized_data(
1233 memory_mapping,
1234 caller_account.vm_data_addr,
1235 post_len as u64,
1236 stricter_abi_and_runtime_constraints,
1237 invoke_context.account_data_direct_mapping,
1238 )?;
1239 }
1240 *caller_account.ref_to_len_in_vm = post_len as u64;
1242
1243 let serialized_len_ptr = translate_type_mut::<u64>(
1245 memory_mapping,
1246 caller_account
1247 .vm_data_addr
1248 .saturating_sub(std::mem::size_of::<u64>() as u64),
1249 check_aligned,
1250 )?;
1251 *serialized_len_ptr = post_len as u64;
1252 }
1253
1254 if !(stricter_abi_and_runtime_constraints && invoke_context.account_data_direct_mapping) {
1255 let to_slice = &mut caller_account.serialized_data;
1257 let from_slice = callee_account
1258 .get_data()
1259 .get(0..post_len)
1260 .ok_or(SyscallError::InvalidLength)?;
1261 if to_slice.len() != from_slice.len() {
1262 return Err(Box::new(InstructionError::AccountDataTooSmall));
1263 }
1264 to_slice.copy_from_slice(from_slice);
1265 }
1266
1267 Ok(())
1268}
1269
1270#[allow(clippy::indexing_slicing)]
1271#[allow(clippy::arithmetic_side_effects)]
1272#[cfg(test)]
1273mod tests {
1274 use {
1275 super::*,
1276 assert_matches::assert_matches,
1277 solana_account::{Account, AccountSharedData, ReadableAccount},
1278 solana_instruction::Instruction,
1279 solana_program_runtime::{
1280 invoke_context::{BpfAllocator, SerializedAccountMetadata, SyscallContext},
1281 with_mock_invoke_context_with_feature_set,
1282 },
1283 solana_sbpf::{
1284 ebpf::MM_INPUT_START, memory_region::MemoryRegion, program::SBPFVersion, vm::Config,
1285 },
1286 solana_sdk_ids::system_program,
1287 solana_transaction_context::{InstructionAccount, TransactionAccount},
1288 std::{
1289 cell::{Cell, RefCell},
1290 mem, ptr,
1291 rc::Rc,
1292 slice,
1293 },
1294 test_case::test_matrix,
1295 };
1296
1297 macro_rules! mock_invoke_context {
1298 ($invoke_context:ident,
1299 $transaction_context:ident,
1300 $instruction_data:expr,
1301 $transaction_accounts:expr,
1302 $program_account:expr,
1303 $instruction_accounts:expr) => {
1304 let instruction_data = $instruction_data;
1305 let instruction_accounts = $instruction_accounts
1306 .iter()
1307 .map(|index_in_transaction| {
1308 InstructionAccount::new(
1309 *index_in_transaction as IndexOfAccount,
1310 false,
1311 $transaction_accounts[*index_in_transaction as usize].2,
1312 )
1313 })
1314 .collect::<Vec<_>>();
1315 let transaction_accounts = $transaction_accounts
1316 .into_iter()
1317 .map(|a| (a.0, a.1))
1318 .collect::<Vec<TransactionAccount>>();
1319 let mut feature_set = SVMFeatureSet::all_enabled();
1320 feature_set.stricter_abi_and_runtime_constraints = false;
1321 let feature_set = &feature_set;
1322 with_mock_invoke_context_with_feature_set!(
1323 $invoke_context,
1324 $transaction_context,
1325 feature_set,
1326 transaction_accounts
1327 );
1328 $invoke_context
1329 .transaction_context
1330 .configure_next_instruction_for_tests(
1331 $program_account,
1332 instruction_accounts,
1333 instruction_data,
1334 )
1335 .unwrap();
1336 $invoke_context.push().unwrap();
1337 };
1338 }
1339
1340 macro_rules! borrow_instruction_account {
1341 ($borrowed_account:ident, $invoke_context:expr, $index:expr) => {
1342 let instruction_context = $invoke_context
1343 .transaction_context
1344 .get_current_instruction_context()
1345 .unwrap();
1346 let $borrowed_account = instruction_context
1347 .try_borrow_instruction_account($index)
1348 .unwrap();
1349 };
1350 }
1351
1352 #[test]
1353 fn test_translate_instruction() {
1354 let transaction_accounts =
1355 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1356 mock_invoke_context!(
1357 invoke_context,
1358 transaction_context,
1359 b"instruction data",
1360 transaction_accounts,
1361 0,
1362 &[1]
1363 );
1364
1365 let program_id = Pubkey::new_unique();
1366 let accounts = vec![AccountMeta {
1367 pubkey: Pubkey::new_unique(),
1368 is_signer: true,
1369 is_writable: false,
1370 }];
1371 let data = b"ins data".to_vec();
1372 let vm_addr = MM_INPUT_START;
1373 let (_mem, region) = MockInstruction {
1374 program_id,
1375 accounts: accounts.clone(),
1376 data: data.clone(),
1377 }
1378 .into_region(vm_addr);
1379
1380 let config = Config {
1381 aligned_memory_mapping: false,
1382 ..Config::default()
1383 };
1384 let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1385
1386 let ins = SyscallInvokeSignedRust::translate_instruction(
1387 vm_addr,
1388 &memory_mapping,
1389 &mut invoke_context,
1390 true, )
1392 .unwrap();
1393 assert_eq!(ins.program_id, program_id);
1394 assert_eq!(ins.accounts, accounts);
1395 assert_eq!(ins.data, data);
1396 }
1397
1398 #[test]
1399 fn test_translate_signers() {
1400 let transaction_accounts =
1401 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1402 mock_invoke_context!(
1403 invoke_context,
1404 transaction_context,
1405 b"instruction data",
1406 transaction_accounts,
1407 0,
1408 &[1]
1409 );
1410
1411 let program_id = Pubkey::new_unique();
1412 let (derived_key, bump_seed) = Pubkey::find_program_address(&[b"foo"], &program_id);
1413
1414 let vm_addr = MM_INPUT_START;
1415 let (_mem, region) = mock_signers(&[b"foo", &[bump_seed]], vm_addr);
1416
1417 let config = Config {
1418 aligned_memory_mapping: false,
1419 ..Config::default()
1420 };
1421 let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1422
1423 let signers = SyscallInvokeSignedRust::translate_signers(
1424 &program_id,
1425 vm_addr,
1426 1,
1427 &memory_mapping,
1428 true, )
1430 .unwrap();
1431 assert_eq!(signers[0], derived_key);
1432 }
1433
1434 #[test]
1435 fn test_caller_account_from_account_info() {
1436 let transaction_accounts =
1437 transaction_with_one_writable_instruction_account(b"foo".to_vec());
1438 let account = transaction_accounts[1].1.clone();
1439 mock_invoke_context!(
1440 invoke_context,
1441 transaction_context,
1442 b"instruction data",
1443 transaction_accounts,
1444 0,
1445 &[1]
1446 );
1447
1448 let key = Pubkey::new_unique();
1449 let vm_addr = MM_INPUT_START;
1450 let (_mem, region, account_metadata) =
1451 MockAccountInfo::new(key, &account).into_region(vm_addr);
1452
1453 let config = Config {
1454 aligned_memory_mapping: false,
1455 ..Config::default()
1456 };
1457 let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1458
1459 let account_info = translate_type::<AccountInfo>(&memory_mapping, vm_addr, false).unwrap();
1460
1461 let caller_account = CallerAccount::from_account_info(
1462 &invoke_context,
1463 &memory_mapping,
1464 true, vm_addr,
1466 account_info,
1467 &account_metadata,
1468 )
1469 .unwrap();
1470 assert_eq!(*caller_account.lamports, account.lamports());
1471 assert_eq!(caller_account.owner, account.owner());
1472 assert_eq!(caller_account.original_data_len, account.data().len());
1473 assert_eq!(
1474 *caller_account.ref_to_len_in_vm as usize,
1475 account.data().len()
1476 );
1477 assert_eq!(caller_account.serialized_data, account.data());
1478 }
1479
1480 #[test_matrix([false, true])]
1481 fn test_update_caller_account_lamports_owner(stricter_abi_and_runtime_constraints: bool) {
1482 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
1483 let account = transaction_accounts[1].1.clone();
1484 mock_invoke_context!(
1485 invoke_context,
1486 transaction_context,
1487 b"instruction data",
1488 transaction_accounts,
1489 0,
1490 &[1]
1491 );
1492
1493 let mut mock_caller_account =
1494 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
1495
1496 let config = Config {
1497 aligned_memory_mapping: false,
1498 ..Config::default()
1499 };
1500 let memory_mapping = MemoryMapping::new(
1501 mock_caller_account.regions.split_off(0),
1502 &config,
1503 SBPFVersion::V3,
1504 )
1505 .unwrap();
1506
1507 let mut caller_account = mock_caller_account.caller_account();
1508 let instruction_context = invoke_context
1509 .transaction_context
1510 .get_current_instruction_context()
1511 .unwrap();
1512 let mut callee_account = instruction_context
1513 .try_borrow_instruction_account(0)
1514 .unwrap();
1515 callee_account.set_lamports(42).unwrap();
1516 callee_account
1517 .set_owner(Pubkey::new_unique().as_ref())
1518 .unwrap();
1519
1520 update_caller_account(
1521 &invoke_context,
1522 &memory_mapping,
1523 true, &mut caller_account,
1525 &mut callee_account,
1526 stricter_abi_and_runtime_constraints,
1527 )
1528 .unwrap();
1529
1530 assert_eq!(*caller_account.lamports, 42);
1531 assert_eq!(caller_account.owner, callee_account.get_owner());
1532 }
1533
1534 #[test]
1535 fn test_update_caller_account_data() {
1536 let transaction_accounts =
1537 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1538 let account = transaction_accounts[1].1.clone();
1539 let original_data_len = account.data().len();
1540
1541 mock_invoke_context!(
1542 invoke_context,
1543 transaction_context,
1544 b"instruction data",
1545 transaction_accounts,
1546 0,
1547 &[1]
1548 );
1549
1550 let mut mock_caller_account =
1551 MockCallerAccount::new(account.lamports(), *account.owner(), account.data(), false);
1552
1553 let config = Config {
1554 aligned_memory_mapping: false,
1555 ..Config::default()
1556 };
1557 let memory_mapping = MemoryMapping::new(
1558 mock_caller_account.regions.clone(),
1559 &config,
1560 SBPFVersion::V3,
1561 )
1562 .unwrap();
1563
1564 let data_slice = mock_caller_account.data_slice();
1565 let len_ptr = unsafe {
1566 data_slice
1567 .as_ptr()
1568 .offset(-(mem::size_of::<u64>() as isize))
1569 };
1570 let serialized_len = || unsafe { *len_ptr.cast::<u64>() as usize };
1571 let mut caller_account = mock_caller_account.caller_account();
1572 let instruction_context = invoke_context
1573 .transaction_context
1574 .get_current_instruction_context()
1575 .unwrap();
1576 let mut callee_account = instruction_context
1577 .try_borrow_instruction_account(0)
1578 .unwrap();
1579
1580 for (new_value, expected_realloc_size) in [
1581 (b"foo".to_vec(), MAX_PERMITTED_DATA_INCREASE + 3),
1582 (b"foobaz".to_vec(), MAX_PERMITTED_DATA_INCREASE),
1583 (b"foobazbad".to_vec(), MAX_PERMITTED_DATA_INCREASE - 3),
1584 ] {
1585 assert_eq!(caller_account.serialized_data, callee_account.get_data());
1586 callee_account.set_data_from_slice(&new_value).unwrap();
1587
1588 update_caller_account(
1589 &invoke_context,
1590 &memory_mapping,
1591 true, &mut caller_account,
1593 &mut callee_account,
1594 false,
1595 )
1596 .unwrap();
1597
1598 let data_len = callee_account.get_data().len();
1599 assert_eq!(data_len, *caller_account.ref_to_len_in_vm as usize);
1600 assert_eq!(data_len, serialized_len());
1601 assert_eq!(data_len, caller_account.serialized_data.len());
1602 assert_eq!(
1603 callee_account.get_data(),
1604 &caller_account.serialized_data[..data_len]
1605 );
1606 assert_eq!(data_slice[data_len..].len(), expected_realloc_size);
1607 assert!(is_zeroed(&data_slice[data_len..]));
1608 }
1609
1610 callee_account
1611 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE)
1612 .unwrap();
1613 update_caller_account(
1614 &invoke_context,
1615 &memory_mapping,
1616 true, &mut caller_account,
1618 &mut callee_account,
1619 false,
1620 )
1621 .unwrap();
1622 let data_len = callee_account.get_data().len();
1623 assert_eq!(data_slice[data_len..].len(), 0);
1624 assert!(is_zeroed(&data_slice[data_len..]));
1625
1626 callee_account
1627 .set_data_length(original_data_len + MAX_PERMITTED_DATA_INCREASE + 1)
1628 .unwrap();
1629 assert_matches!(
1630 update_caller_account(
1631 &invoke_context,
1632 &memory_mapping,
1633 true, &mut caller_account,
1635 &mut callee_account,
1636 false,
1637 ),
1638 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1639 );
1640
1641 callee_account.set_data_length(0).unwrap();
1643 callee_account
1644 .set_owner(system_program::id().as_ref())
1645 .unwrap();
1646 update_caller_account(
1647 &invoke_context,
1648 &memory_mapping,
1649 true, &mut caller_account,
1651 &mut callee_account,
1652 false,
1653 )
1654 .unwrap();
1655 let data_len = callee_account.get_data().len();
1656 assert_eq!(data_len, 0);
1657 }
1658
1659 #[test_matrix([false, true])]
1660 fn test_update_callee_account_lamports_owner(stricter_abi_and_runtime_constraints: bool) {
1661 let transaction_accounts = transaction_with_one_writable_instruction_account(vec![]);
1662 let account = transaction_accounts[1].1.clone();
1663
1664 mock_invoke_context!(
1665 invoke_context,
1666 transaction_context,
1667 b"instruction data",
1668 transaction_accounts,
1669 0,
1670 &[1]
1671 );
1672
1673 let mut mock_caller_account =
1674 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
1675
1676 let caller_account = mock_caller_account.caller_account();
1677
1678 borrow_instruction_account!(callee_account, invoke_context, 0);
1679
1680 *caller_account.lamports = 42;
1681 *caller_account.owner = Pubkey::new_unique();
1682
1683 update_callee_account(
1684 true, &caller_account,
1686 callee_account,
1687 stricter_abi_and_runtime_constraints,
1688 true, )
1690 .unwrap();
1691
1692 borrow_instruction_account!(callee_account, invoke_context, 0);
1693 assert_eq!(callee_account.get_lamports(), 42);
1694 assert_eq!(caller_account.owner, callee_account.get_owner());
1695 }
1696
1697 #[test_matrix([false, true])]
1698 fn test_update_callee_account_data_writable(stricter_abi_and_runtime_constraints: bool) {
1699 let transaction_accounts =
1700 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1701 let account = transaction_accounts[1].1.clone();
1702
1703 mock_invoke_context!(
1704 invoke_context,
1705 transaction_context,
1706 b"instruction data",
1707 transaction_accounts,
1708 0,
1709 &[1]
1710 );
1711
1712 let mut mock_caller_account =
1713 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
1714
1715 let mut caller_account = mock_caller_account.caller_account();
1716 borrow_instruction_account!(callee_account, invoke_context, 0);
1717
1718 caller_account.serialized_data[0] = b'b';
1720 update_callee_account(
1721 true, &caller_account,
1723 callee_account,
1724 false, false, )
1727 .unwrap();
1728 borrow_instruction_account!(callee_account, invoke_context, 0);
1729 assert_eq!(callee_account.get_data(), b"boobar");
1730
1731 let mut data = b"foobarbaz".to_vec();
1733 *caller_account.ref_to_len_in_vm = data.len() as u64;
1734 caller_account.serialized_data = &mut data;
1735 assert_eq!(
1736 update_callee_account(
1737 true, &caller_account,
1739 callee_account,
1740 stricter_abi_and_runtime_constraints,
1741 true, )
1743 .unwrap(),
1744 stricter_abi_and_runtime_constraints,
1745 );
1746
1747 let mut data = b"baz".to_vec();
1749 *caller_account.ref_to_len_in_vm = data.len() as u64;
1750 caller_account.serialized_data = &mut data;
1751 borrow_instruction_account!(callee_account, invoke_context, 0);
1752 assert_eq!(
1753 update_callee_account(
1754 true, &caller_account,
1756 callee_account,
1757 stricter_abi_and_runtime_constraints,
1758 true, )
1760 .unwrap(),
1761 stricter_abi_and_runtime_constraints,
1762 );
1763
1764 let mut data = Vec::new();
1766 caller_account.serialized_data = &mut data;
1767 *caller_account.ref_to_len_in_vm = 0;
1768 let mut owner = system_program::id();
1769 caller_account.owner = &mut owner;
1770 borrow_instruction_account!(callee_account, invoke_context, 0);
1771 update_callee_account(
1772 true, &caller_account,
1774 callee_account,
1775 stricter_abi_and_runtime_constraints,
1776 true, )
1778 .unwrap();
1779 borrow_instruction_account!(callee_account, invoke_context, 0);
1780 assert_eq!(callee_account.get_data(), b"");
1781
1782 *caller_account.ref_to_len_in_vm = (7 + MAX_PERMITTED_DATA_INCREASE) as u64;
1784 let result = update_callee_account(
1785 true, &caller_account,
1787 callee_account,
1788 stricter_abi_and_runtime_constraints,
1789 true, );
1791 if stricter_abi_and_runtime_constraints {
1792 assert_matches!(
1793 result,
1794 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::InvalidRealloc
1795 );
1796 } else {
1797 result.unwrap();
1798 }
1799 }
1800
1801 #[test_matrix([false, true])]
1802 fn test_update_callee_account_data_readonly(stricter_abi_and_runtime_constraints: bool) {
1803 let transaction_accounts =
1804 transaction_with_one_readonly_instruction_account(b"foobar".to_vec());
1805 let account = transaction_accounts[1].1.clone();
1806
1807 mock_invoke_context!(
1808 invoke_context,
1809 transaction_context,
1810 b"instruction data",
1811 transaction_accounts,
1812 0,
1813 &[1]
1814 );
1815
1816 let mut mock_caller_account =
1817 MockCallerAccount::new(1234, *account.owner(), account.data(), false);
1818 let mut caller_account = mock_caller_account.caller_account();
1819 borrow_instruction_account!(callee_account, invoke_context, 0);
1820
1821 caller_account.serialized_data[0] = b'b';
1823 assert_matches!(
1824 update_callee_account(
1825 true, &caller_account,
1827 callee_account,
1828 false, false, ),
1831 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::ExternalAccountDataModified
1832 );
1833
1834 let mut data = b"foobarbaz".to_vec();
1836 *caller_account.ref_to_len_in_vm = data.len() as u64;
1837 caller_account.serialized_data = &mut data;
1838 borrow_instruction_account!(callee_account, invoke_context, 0);
1839 assert_matches!(
1840 update_callee_account(
1841 true, &caller_account,
1843 callee_account,
1844 stricter_abi_and_runtime_constraints,
1845 true, ),
1847 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::AccountDataSizeChanged
1848 );
1849
1850 let mut data = b"baz".to_vec();
1852 *caller_account.ref_to_len_in_vm = data.len() as u64;
1853 caller_account.serialized_data = &mut data;
1854 borrow_instruction_account!(callee_account, invoke_context, 0);
1855 assert_matches!(
1856 update_callee_account(
1857 true, &caller_account,
1859 callee_account,
1860 stricter_abi_and_runtime_constraints,
1861 true, ),
1863 Err(error) if error.downcast_ref::<InstructionError>().unwrap() == &InstructionError::AccountDataSizeChanged
1864 );
1865 }
1866
1867 #[test]
1868 fn test_translate_accounts_rust() {
1869 let transaction_accounts =
1870 transaction_with_one_writable_instruction_account(b"foobar".to_vec());
1871 let account = transaction_accounts[1].1.clone();
1872 let key = transaction_accounts[1].0;
1873 let original_data_len = account.data().len();
1874
1875 let vm_addr = MM_INPUT_START;
1876 let (_mem, region, account_metadata) =
1877 MockAccountInfo::new(key, &account).into_region(vm_addr);
1878
1879 let config = Config {
1880 aligned_memory_mapping: false,
1881 ..Config::default()
1882 };
1883 let memory_mapping = MemoryMapping::new(vec![region], &config, SBPFVersion::V3).unwrap();
1884
1885 mock_invoke_context!(
1886 invoke_context,
1887 transaction_context,
1888 b"instruction data",
1889 transaction_accounts,
1890 0,
1891 &[1, 1]
1892 );
1893
1894 invoke_context
1895 .set_syscall_context(SyscallContext {
1896 allocator: BpfAllocator::new(solana_program_entrypoint::HEAP_LENGTH as u64),
1897 accounts_metadata: vec![account_metadata],
1898 trace_log: Vec::new(),
1899 })
1900 .unwrap();
1901
1902 invoke_context
1903 .transaction_context
1904 .configure_next_instruction_for_tests(
1905 0,
1906 vec![
1907 InstructionAccount::new(1, false, true),
1908 InstructionAccount::new(1, false, true),
1909 ],
1910 &[],
1911 )
1912 .unwrap();
1913 let accounts = SyscallInvokeSignedRust::translate_accounts(
1914 vm_addr,
1915 1,
1916 &memory_mapping,
1917 &mut invoke_context,
1918 true, )
1920 .unwrap();
1921 assert_eq!(accounts.len(), 1);
1922 let caller_account = &accounts[0].caller_account;
1923 assert_eq!(caller_account.serialized_data, account.data());
1924 assert_eq!(caller_account.original_data_len, original_data_len);
1925 }
1926
1927 type TestTransactionAccount = (Pubkey, AccountSharedData, bool);
1928 struct MockCallerAccount {
1929 lamports: u64,
1930 owner: Pubkey,
1931 vm_addr: u64,
1932 data: Vec<u8>,
1933 len: u64,
1934 regions: Vec<MemoryRegion>,
1935 stricter_abi_and_runtime_constraints: bool,
1936 }
1937
1938 impl MockCallerAccount {
1939 fn new(
1940 lamports: u64,
1941 owner: Pubkey,
1942 data: &[u8],
1943 stricter_abi_and_runtime_constraints: bool,
1944 ) -> MockCallerAccount {
1945 let vm_addr = MM_INPUT_START;
1946 let mut region_addr = vm_addr;
1947 let region_len = mem::size_of::<u64>()
1948 + if stricter_abi_and_runtime_constraints {
1949 0
1950 } else {
1951 data.len() + MAX_PERMITTED_DATA_INCREASE
1952 };
1953 let mut d = vec![0; region_len];
1954 let mut regions = vec![];
1955
1956 unsafe { ptr::write_unaligned::<u64>(d.as_mut_ptr().cast(), data.len() as u64) };
1958
1959 if !stricter_abi_and_runtime_constraints {
1961 d[mem::size_of::<u64>()..][..data.len()].copy_from_slice(data);
1962 }
1963
1964 regions.push(MemoryRegion::new_writable(&mut d[..region_len], vm_addr));
1966 region_addr += region_len as u64;
1967
1968 if stricter_abi_and_runtime_constraints {
1969 regions.push(MemoryRegion::new_readonly(data, region_addr));
1971 region_addr += data.len() as u64;
1972
1973 regions.push(MemoryRegion::new_writable(
1975 &mut d[mem::size_of::<u64>()..],
1976 region_addr,
1977 ));
1978 } else {
1979 d.truncate(mem::size_of::<u64>() + data.len());
1981 }
1982
1983 MockCallerAccount {
1984 lamports,
1985 owner,
1986 vm_addr,
1987 data: d,
1988 len: data.len() as u64,
1989 regions,
1990 stricter_abi_and_runtime_constraints,
1991 }
1992 }
1993
1994 fn data_slice<'a>(&self) -> &'a [u8] {
1995 unsafe {
1997 slice::from_raw_parts(
1998 self.data[mem::size_of::<u64>()..].as_ptr(),
1999 self.data.capacity() - mem::size_of::<u64>(),
2000 )
2001 }
2002 }
2003
2004 fn caller_account(&mut self) -> CallerAccount {
2005 let data = if self.stricter_abi_and_runtime_constraints {
2006 &mut []
2007 } else {
2008 &mut self.data[mem::size_of::<u64>()..]
2009 };
2010 CallerAccount {
2011 lamports: &mut self.lamports,
2012 owner: &mut self.owner,
2013 original_data_len: self.len as usize,
2014 serialized_data: data,
2015 vm_data_addr: self.vm_addr + mem::size_of::<u64>() as u64,
2016 ref_to_len_in_vm: &mut self.len,
2017 }
2018 }
2019 }
2020
2021 fn transaction_with_one_writable_instruction_account(
2022 data: Vec<u8>,
2023 ) -> Vec<TestTransactionAccount> {
2024 let program_id = Pubkey::new_unique();
2025 let account = AccountSharedData::from(Account {
2026 lamports: 1,
2027 data,
2028 owner: program_id,
2029 executable: false,
2030 rent_epoch: 100,
2031 });
2032 vec![
2033 (
2034 program_id,
2035 AccountSharedData::from(Account {
2036 lamports: 0,
2037 data: vec![],
2038 owner: bpf_loader::id(),
2039 executable: true,
2040 rent_epoch: 0,
2041 }),
2042 false,
2043 ),
2044 (Pubkey::new_unique(), account, true),
2045 ]
2046 }
2047
2048 fn transaction_with_one_readonly_instruction_account(
2049 data: Vec<u8>,
2050 ) -> Vec<TestTransactionAccount> {
2051 let program_id = Pubkey::new_unique();
2052 let account_owner = Pubkey::new_unique();
2053 let account = AccountSharedData::from(Account {
2054 lamports: 1,
2055 data,
2056 owner: account_owner,
2057 executable: false,
2058 rent_epoch: 100,
2059 });
2060 vec![
2061 (
2062 program_id,
2063 AccountSharedData::from(Account {
2064 lamports: 0,
2065 data: vec![],
2066 owner: bpf_loader::id(),
2067 executable: true,
2068 rent_epoch: 0,
2069 }),
2070 false,
2071 ),
2072 (Pubkey::new_unique(), account, true),
2073 ]
2074 }
2075
2076 struct MockInstruction {
2077 program_id: Pubkey,
2078 accounts: Vec<AccountMeta>,
2079 data: Vec<u8>,
2080 }
2081
2082 impl MockInstruction {
2083 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
2084 let accounts_len = mem::size_of::<AccountMeta>() * self.accounts.len();
2085
2086 let size = mem::size_of::<StableInstruction>() + accounts_len + self.data.len();
2087
2088 let mut data = vec![0; size];
2089
2090 let vm_addr = vm_addr as usize;
2091 let accounts_addr = vm_addr + mem::size_of::<StableInstruction>();
2092 let data_addr = accounts_addr + accounts_len;
2093
2094 let ins = Instruction {
2095 program_id: self.program_id,
2096 accounts: unsafe {
2097 Vec::from_raw_parts(
2098 accounts_addr as *mut _,
2099 self.accounts.len(),
2100 self.accounts.len(),
2101 )
2102 },
2103 data: unsafe {
2104 Vec::from_raw_parts(data_addr as *mut _, self.data.len(), self.data.len())
2105 },
2106 };
2107 let ins = StableInstruction::from(ins);
2108
2109 unsafe {
2110 ptr::write_unaligned(data.as_mut_ptr().cast(), ins);
2111 data[accounts_addr - vm_addr..][..accounts_len].copy_from_slice(
2112 slice::from_raw_parts(self.accounts.as_ptr().cast(), accounts_len),
2113 );
2114 data[data_addr - vm_addr..].copy_from_slice(&self.data);
2115 }
2116
2117 let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2118 (data, region)
2119 }
2120 }
2121
2122 fn mock_signers(signers: &[&[u8]], vm_addr: u64) -> (Vec<u8>, MemoryRegion) {
2123 let vm_addr = vm_addr as usize;
2124
2125 let fat_ptr_size_of_slice = mem::size_of::<&[()]>(); let singers_length = signers.len();
2128 let sum_signers_data_length: usize = signers.iter().map(|s| s.len()).sum();
2129
2130 let total_size = fat_ptr_size_of_slice
2132 + singers_length * fat_ptr_size_of_slice
2133 + sum_signers_data_length;
2134 let mut data = vec![0; total_size];
2135
2136 data[..fat_ptr_size_of_slice / 2]
2148 .clone_from_slice(&(fat_ptr_size_of_slice + vm_addr).to_le_bytes());
2149 data[fat_ptr_size_of_slice / 2..fat_ptr_size_of_slice]
2150 .clone_from_slice(&(singers_length).to_le_bytes());
2151
2152 let (mut p1, mut p2) = (
2154 fat_ptr_size_of_slice,
2155 fat_ptr_size_of_slice + singers_length * fat_ptr_size_of_slice,
2156 );
2157 for signer in signers.iter() {
2158 let signer_length = signer.len();
2159
2160 data[p1..p1 + fat_ptr_size_of_slice / 2]
2162 .clone_from_slice(&(p2 + vm_addr).to_le_bytes());
2163 data[p1 + fat_ptr_size_of_slice / 2..p1 + fat_ptr_size_of_slice]
2164 .clone_from_slice(&(signer_length).to_le_bytes());
2165 p1 += fat_ptr_size_of_slice;
2166
2167 data[p2..p2 + signer_length].clone_from_slice(signer);
2169 p2 += signer_length;
2170 }
2171
2172 let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2173 (data, region)
2174 }
2175
2176 struct MockAccountInfo<'a> {
2177 key: Pubkey,
2178 is_signer: bool,
2179 is_writable: bool,
2180 lamports: u64,
2181 data: &'a [u8],
2182 owner: Pubkey,
2183 executable: bool,
2184 _unused: u64,
2185 }
2186
2187 impl MockAccountInfo<'_> {
2188 fn new(key: Pubkey, account: &AccountSharedData) -> MockAccountInfo {
2189 MockAccountInfo {
2190 key,
2191 is_signer: false,
2192 is_writable: false,
2193 lamports: account.lamports(),
2194 data: account.data(),
2195 owner: *account.owner(),
2196 executable: account.executable(),
2197 _unused: account.rent_epoch(),
2198 }
2199 }
2200
2201 fn into_region(self, vm_addr: u64) -> (Vec<u8>, MemoryRegion, SerializedAccountMetadata) {
2202 let size = mem::size_of::<AccountInfo>()
2203 + mem::size_of::<Pubkey>() * 2
2204 + mem::size_of::<RcBox<RefCell<&mut u64>>>()
2205 + mem::size_of::<u64>()
2206 + mem::size_of::<RcBox<RefCell<&mut [u8]>>>()
2207 + self.data.len();
2208 let mut data = vec![0; size];
2209
2210 let vm_addr = vm_addr as usize;
2211 let key_addr = vm_addr + mem::size_of::<AccountInfo>();
2212 let lamports_cell_addr = key_addr + mem::size_of::<Pubkey>();
2213 let lamports_addr = lamports_cell_addr + mem::size_of::<RcBox<RefCell<&mut u64>>>();
2214 let owner_addr = lamports_addr + mem::size_of::<u64>();
2215 let data_cell_addr = owner_addr + mem::size_of::<Pubkey>();
2216 let data_addr = data_cell_addr + mem::size_of::<RcBox<RefCell<&mut [u8]>>>();
2217
2218 #[allow(deprecated)]
2219 #[allow(clippy::used_underscore_binding)]
2220 let info = AccountInfo {
2221 key: unsafe { (key_addr as *const Pubkey).as_ref() }.unwrap(),
2222 is_signer: self.is_signer,
2223 is_writable: self.is_writable,
2224 lamports: unsafe {
2225 Rc::from_raw((lamports_cell_addr + RcBox::<&mut u64>::VALUE_OFFSET) as *const _)
2226 },
2227 data: unsafe {
2228 Rc::from_raw((data_cell_addr + RcBox::<&mut [u8]>::VALUE_OFFSET) as *const _)
2229 },
2230 owner: unsafe { (owner_addr as *const Pubkey).as_ref() }.unwrap(),
2231 executable: self.executable,
2232 _unused: self._unused,
2233 };
2234
2235 unsafe {
2236 ptr::write_unaligned(data.as_mut_ptr().cast(), info);
2237 ptr::write_unaligned(
2238 (data.as_mut_ptr() as usize + key_addr - vm_addr) as *mut _,
2239 self.key,
2240 );
2241 ptr::write_unaligned(
2242 (data.as_mut_ptr() as usize + lamports_cell_addr - vm_addr) as *mut _,
2243 RcBox::new(RefCell::new((lamports_addr as *mut u64).as_mut().unwrap())),
2244 );
2245 ptr::write_unaligned(
2246 (data.as_mut_ptr() as usize + lamports_addr - vm_addr) as *mut _,
2247 self.lamports,
2248 );
2249 ptr::write_unaligned(
2250 (data.as_mut_ptr() as usize + owner_addr - vm_addr) as *mut _,
2251 self.owner,
2252 );
2253 ptr::write_unaligned(
2254 (data.as_mut_ptr() as usize + data_cell_addr - vm_addr) as *mut _,
2255 RcBox::new(RefCell::new(slice::from_raw_parts_mut(
2256 data_addr as *mut u8,
2257 self.data.len(),
2258 ))),
2259 );
2260 data[data_addr - vm_addr..].copy_from_slice(self.data);
2261 }
2262
2263 let region = MemoryRegion::new_writable(data.as_mut_slice(), vm_addr as u64);
2264 (
2265 data,
2266 region,
2267 SerializedAccountMetadata {
2268 original_data_len: self.data.len(),
2269 vm_key_addr: key_addr as u64,
2270 vm_lamports_addr: lamports_addr as u64,
2271 vm_owner_addr: owner_addr as u64,
2272 vm_data_addr: data_addr as u64,
2273 },
2274 )
2275 }
2276 }
2277
2278 #[repr(C)]
2279 struct RcBox<T> {
2280 strong: Cell<usize>,
2281 weak: Cell<usize>,
2282 value: T,
2283 }
2284
2285 impl<T> RcBox<T> {
2286 const VALUE_OFFSET: usize = mem::size_of::<Cell<usize>>() * 2;
2287 fn new(value: T) -> RcBox<T> {
2288 RcBox {
2289 strong: Cell::new(0),
2290 weak: Cell::new(0),
2291 value,
2292 }
2293 }
2294 }
2295
2296 fn is_zeroed(data: &[u8]) -> bool {
2297 data.iter().all(|b| *b == 0)
2298 }
2299}