diff --git a/programs/sbf/rust/invoke/src/instructions.rs b/programs/sbf/rust/invoke/src/instructions.rs index d7d833009c69ce..2da46b9ef21816 100644 --- a/programs/sbf/rust/invoke/src/instructions.rs +++ b/programs/sbf/rust/invoke/src/instructions.rs @@ -37,6 +37,7 @@ pub const TEST_CPI_INVALID_KEY_POINTER: u8 = 34; pub const TEST_CPI_INVALID_OWNER_POINTER: u8 = 35; pub const TEST_CPI_INVALID_LAMPORTS_POINTER: u8 = 36; pub const TEST_CPI_INVALID_DATA_POINTER: u8 = 37; +pub const TEST_WRITE_ACCOUNT: u8 = 38; pub const MINT_INDEX: usize = 0; pub const ARGUMENT_INDEX: usize = 1; diff --git a/programs/sbf/rust/invoke/src/processor.rs b/programs/sbf/rust/invoke/src/processor.rs index 81f81ec0678465..b6d592f0f88a61 100644 --- a/programs/sbf/rust/invoke/src/processor.rs +++ b/programs/sbf/rust/invoke/src/processor.rs @@ -1220,6 +1220,13 @@ fn process_instruction( ) .unwrap(); } + TEST_WRITE_ACCOUNT => { + msg!("TEST_WRITE_ACCOUNT"); + let target_account_index = instruction_data[1] as usize; + let target_account = &accounts[target_account_index]; + let byte_index = usize::from_le_bytes(instruction_data[2..10].try_into().unwrap()); + target_account.data.borrow_mut()[byte_index] = instruction_data[10]; + } _ => panic!("unexpected program data"), } diff --git a/programs/sbf/tests/programs.rs b/programs/sbf/tests/programs.rs index 8e5c7aa104c20b..f723c8f08ea20b 100644 --- a/programs/sbf/tests/programs.rs +++ b/programs/sbf/tests/programs.rs @@ -4440,3 +4440,56 @@ fn test_cpi_invalid_account_info_pointers() { } } } + +#[test] +#[cfg(feature = "sbf_rust")] +fn test_deny_executable_write() { + solana_logger::setup(); + + let GenesisConfigInfo { + genesis_config, + mint_keypair, + .. + } = create_genesis_config(100_123_456_789); + + for direct_mapping in [false, true] { + let mut bank = Bank::new_for_tests(&genesis_config); + let feature_set = Arc::make_mut(&mut bank.feature_set); + // by default test banks have all features enabled, so we only need to + // disable when needed + if !direct_mapping { + feature_set.deactivate(&feature_set::bpf_account_data_direct_mapping::id()); + } + let bank = Arc::new(bank); + let mut bank_client = BankClient::new_shared(bank); + + let (_bank, invoke_program_id) = load_program_and_advance_slot( + &mut bank_client, + &bpf_loader::id(), + &mint_keypair, + "solana_sbf_rust_invoke", + ); + + let account_keypair = Keypair::new(); + let mint_pubkey = mint_keypair.pubkey(); + let account_metas = vec![ + AccountMeta::new(mint_pubkey, true), + AccountMeta::new(account_keypair.pubkey(), false), + AccountMeta::new_readonly(invoke_program_id, false), + ]; + + let mut instruction_data = vec![TEST_WRITE_ACCOUNT, 2]; + instruction_data.extend_from_slice(4usize.to_le_bytes().as_ref()); + instruction_data.push(42); + let instruction = Instruction::new_with_bytes( + invoke_program_id, + &instruction_data, + account_metas.clone(), + ); + let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); + assert_eq!( + result.unwrap_err().unwrap(), + TransactionError::InstructionError(0, InstructionError::ExecutableDataModified) + ); + } +}