diff --git a/genesis-programs/src/lib.rs b/genesis-programs/src/lib.rs index bf7c34d6d7..c24f305f79 100644 --- a/genesis-programs/src/lib.rs +++ b/genesis-programs/src/lib.rs @@ -105,6 +105,7 @@ pub fn get_entered_epoch_callback(operating_mode: OperatingMode) -> EnteredEpoch bank.add_native_program(name, program_id); } } + bank.set_cross_program_support(OperatingMode::Stable != operating_mode); }) } diff --git a/programs/bpf/benches/bpf_loader.rs b/programs/bpf/benches/bpf_loader.rs index d613373e69..8458eecbe2 100644 --- a/programs/bpf/benches/bpf_loader.rs +++ b/programs/bpf/benches/bpf_loader.rs @@ -159,6 +159,9 @@ impl InvokeContext for MockInvokeContext { fn get_logger(&self) -> Rc> { Rc::new(RefCell::new(self.mock_logger.clone())) } + fn is_cross_program_supported(&self) -> bool { + true + } } #[derive(Debug, Default, Clone)] pub struct MockLogger { diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 41d48d0c02..c48874e11c 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -326,6 +326,9 @@ mod tests { fn get_logger(&self) -> Rc> { Rc::new(RefCell::new(self.mock_logger.clone())) } + fn is_cross_program_supported(&self) -> bool { + true + } } #[derive(Debug, Default, Clone)] pub struct MockLogger { diff --git a/programs/bpf_loader/src/syscalls.rs b/programs/bpf_loader/src/syscalls.rs index 3a78a34bde..bf8c1cfa38 100644 --- a/programs/bpf_loader/src/syscalls.rs +++ b/programs/bpf_loader/src/syscalls.rs @@ -794,6 +794,7 @@ fn call<'a>( message_processor.add_program(*program_id, *process_instruction); } message_processor.add_loader(bpf_loader::id(), crate::process_instruction); + message_processor.set_cross_program_support(invoke_context.is_cross_program_supported()); #[allow(clippy::deref_addrof)] match message_processor.process_cross_program_instruction( diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index abb132f405..89b0f35542 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -449,6 +449,9 @@ impl Bank { bank.update_rent(); bank.update_epoch_schedule(); bank.update_recent_blockhashes(); + if bank.operating_mode == Some(OperatingMode::Stable) { + bank.message_processor.set_cross_program_support(false); + } bank } @@ -1191,6 +1194,11 @@ impl Bank { debug!("Added native program {} under {:?}", name, program_id); } + pub fn set_cross_program_support(&mut self, is_supported: bool) { + self.message_processor + .set_cross_program_support(is_supported); + } + /// Return the last block hash registered. pub fn last_blockhash(&self) -> Hash { self.blockhash_queue.read().unwrap().last_hash() diff --git a/runtime/src/message_processor.rs b/runtime/src/message_processor.rs index 57b250afee..311caf198a 100644 --- a/runtime/src/message_processor.rs +++ b/runtime/src/message_processor.rs @@ -160,6 +160,7 @@ pub struct ThisInvokeContext { pre_accounts: Vec, programs: Vec<(Pubkey, ProcessInstruction)>, logger: Rc>, + is_cross_program_supported: bool, } impl ThisInvokeContext { const MAX_INVOCATION_DEPTH: usize = 5; @@ -169,6 +170,7 @@ impl ThisInvokeContext { pre_accounts: Vec, programs: Vec<(Pubkey, ProcessInstruction)>, log_collector: Option>, + is_cross_program_supported: bool, ) -> Self { let mut program_ids = Vec::with_capacity(Self::MAX_INVOCATION_DEPTH); program_ids.push(*program_id); @@ -178,6 +180,7 @@ impl ThisInvokeContext { pre_accounts, programs, logger: Rc::new(RefCell::new(ThisLogger { log_collector })), + is_cross_program_supported, } } } @@ -225,6 +228,9 @@ impl InvokeContext for ThisInvokeContext { fn get_logger(&self) -> Rc> { self.logger.clone() } + fn is_cross_program_supported(&self) -> bool { + self.is_cross_program_supported + } } pub struct ThisLogger { log_collector: Option>, @@ -244,7 +250,7 @@ impl Logger for ThisLogger { pub type ProcessInstructionWithContext = fn(&Pubkey, &[KeyedAccount], &[u8], &mut dyn InvokeContext) -> Result<(), InstructionError>; -#[derive(Default, Deserialize, Serialize)] +#[derive(Deserialize, Serialize)] pub struct MessageProcessor { #[serde(skip)] programs: Vec<(Pubkey, ProcessInstruction)>, @@ -252,6 +258,18 @@ pub struct MessageProcessor { loaders: Vec<(Pubkey, ProcessInstructionWithContext)>, #[serde(skip)] native_loader: NativeLoader, + #[serde(skip)] + is_cross_program_supported: bool, +} +impl Default for MessageProcessor { + fn default() -> Self { + Self { + programs: vec![], + loaders: vec![], + native_loader: NativeLoader::default(), + is_cross_program_supported: true, + } + } } impl Clone for MessageProcessor { fn clone(&self) -> Self { @@ -259,6 +277,7 @@ impl Clone for MessageProcessor { programs: self.programs.clone(), loaders: self.loaders.clone(), native_loader: NativeLoader::default(), + is_cross_program_supported: self.is_cross_program_supported, } } } @@ -292,6 +311,10 @@ impl MessageProcessor { } } + pub fn set_cross_program_support(&mut self, is_supported: bool) { + self.is_cross_program_supported = is_supported; + } + /// Create the KeyedAccounts that will be passed to the program fn create_keyed_accounts<'a>( message: &'a Message, @@ -369,6 +392,10 @@ impl MessageProcessor { accounts: &[Rc>], invoke_context: &mut dyn InvokeContext, ) -> Result<(), InstructionError> { + if !self.is_cross_program_supported { + return Err(InstructionError::ReentrancyNotAllowed); + } + let instruction = &message.instructions[0]; // Verify the calling program hasn't misbehaved @@ -523,6 +550,7 @@ impl MessageProcessor { pre_accounts, self.programs.clone(), // get rid of clone log_collector, + self.is_cross_program_supported, ); let keyed_accounts = Self::create_keyed_accounts(message, instruction, executable_accounts, accounts)?; @@ -595,8 +623,14 @@ mod tests { true, )) } - let mut invoke_context = - ThisInvokeContext::new(&program_ids[0], Rent::default(), pre_accounts, vec![], None); + let mut invoke_context = ThisInvokeContext::new( + &program_ids[0], + Rent::default(), + pre_accounts, + vec![], + None, + true, + ); // Check call depth increases and has a limit let mut depth_reached = 1; @@ -1365,6 +1399,7 @@ mod tests { vec![owned_preaccount, not_owned_preaccount], vec![], None, + true, ); let metas = vec![ AccountMeta::new(owned_key, false), diff --git a/sdk/src/entrypoint_native.rs b/sdk/src/entrypoint_native.rs index c0a100aefc..931c32dc81 100644 --- a/sdk/src/entrypoint_native.rs +++ b/sdk/src/entrypoint_native.rs @@ -214,6 +214,8 @@ pub trait InvokeContext { fn get_programs(&self) -> &[(Pubkey, ProcessInstruction)]; /// Get this invocation's logger fn get_logger(&self) -> Rc>; + /// Are cross program invocations supported + fn is_cross_program_supported(&self) -> bool; } /// Log messages