diff --git a/programs/bpf/rust/invoke/src/lib.rs b/programs/bpf/rust/invoke/src/lib.rs index b84ead95db..3bee6378b0 100644 --- a/programs/bpf/rust/invoke/src/lib.rs +++ b/programs/bpf/rust/invoke/src/lib.rs @@ -261,8 +261,10 @@ fn process_instruction( )?, accounts[DERIVED_KEY1_INDEX].key ); + let not_native_program_id = Pubkey::new_from_array([6u8; 32]); + assert!(!not_native_program_id.is_native_program_id()); assert_eq!( - Pubkey::create_program_address(&[b"You pass butter"], &Pubkey::default()) + Pubkey::create_program_address(&[b"You pass butter"], ¬_native_program_id) .unwrap_err(), PubkeyError::InvalidSeeds ); @@ -274,8 +276,10 @@ fn process_instruction( Pubkey::try_find_program_address(&[b"You pass butter"], program_id).unwrap(); assert_eq!(&address, accounts[DERIVED_KEY1_INDEX].key); assert_eq!(bump_seed, bump_seed1); + let not_native_program_id = Pubkey::new_from_array([6u8; 32]); + assert!(!not_native_program_id.is_native_program_id()); assert_eq!( - Pubkey::create_program_address(&[b"You pass butter"], &Pubkey::default()) + Pubkey::create_program_address(&[b"You pass butter"], ¬_native_program_id) .unwrap_err(), PubkeyError::InvalidSeeds ); diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 1ec603457f..c19978baf8 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -2197,8 +2197,10 @@ mod tests { let upgrade_authority_address = Pubkey::new_unique(); let buffer_address = Pubkey::new_unique(); let program_address = Pubkey::new_unique(); - let (programdata_address, _) = - Pubkey::find_program_address(&[program_address.as_ref()], &id()); + let (programdata_address, _) = Pubkey::find_program_address( + &[program_address.as_ref()], + &bpf_loader_upgradeable::id(), + ); let spill_address = Pubkey::new_unique(); let upgrade_authority_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique()); @@ -2847,8 +2849,10 @@ mod tests { let new_upgrade_authority_address = Pubkey::new_unique(); let new_upgrade_authority_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique()); let program_address = Pubkey::new_unique(); - let (programdata_address, _) = - Pubkey::find_program_address(&[program_address.as_ref()], &id()); + let (programdata_address, _) = Pubkey::find_program_address( + &[program_address.as_ref()], + &bpf_loader_upgradeable::id(), + ); let programdata_account = AccountSharedData::new_ref( 1, UpgradeableLoaderState::programdata_len(0).unwrap(), diff --git a/programs/config/src/lib.rs b/programs/config/src/lib.rs index e170ef7659..cdf27c61ef 100644 --- a/programs/config/src/lib.rs +++ b/programs/config/src/lib.rs @@ -11,7 +11,7 @@ use solana_sdk::{ short_vec, }; -solana_sdk::declare_id!("Config1111111111111111111111111111111111111"); +pub use solana_sdk::config::program::id; pub trait ConfigState: serde::Serialize + Default { /// Maximum space that the serialized representation will require diff --git a/programs/stake/src/config.rs b/programs/stake/src/config.rs index 473aa9c4a1..9ca3fc0f66 100644 --- a/programs/stake/src/config.rs +++ b/programs/stake/src/config.rs @@ -11,7 +11,7 @@ use solana_sdk::{ }; // stake config ID -solana_sdk::declare_id!("StakeConfig11111111111111111111111111111111"); +pub use solana_sdk::stake::config::{check_id, id}; // means that no more than RATE of current effective stake may be added or subtracted per // epoch diff --git a/programs/stake/src/lib.rs b/programs/stake/src/lib.rs index 23438bf8c8..db7227f2bf 100644 --- a/programs/stake/src/lib.rs +++ b/programs/stake/src/lib.rs @@ -6,7 +6,7 @@ pub mod config; pub mod stake_instruction; pub mod stake_state; -solana_sdk::declare_id!("Stake11111111111111111111111111111111111111"); +pub use solana_sdk::stake::program::{check_id, id}; pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig) -> u64 { config::add_genesis_account(genesis_config) diff --git a/programs/vote/src/lib.rs b/programs/vote/src/lib.rs index 57e55b52dd..0af6995358 100644 --- a/programs/vote/src/lib.rs +++ b/programs/vote/src/lib.rs @@ -12,4 +12,4 @@ extern crate solana_metrics; #[macro_use] extern crate solana_frozen_abi_macro; -solana_sdk::declare_id!("Vote111111111111111111111111111111111111111"); +pub use solana_sdk::vote::program::{check_id, id}; diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 7e79427274..87ae83bad6 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -139,7 +139,7 @@ impl ExecuteTimings { } type BankStatusCache = StatusCache>; -#[frozen_abi(digest = "F3Ubz2Sx973pKSYNHTEmj6LY3te1DKUo3fs3cgzQ1uqJ")] +#[frozen_abi(digest = "HhY4tMP5KZU9fw9VLpMMUikfvNVCLksocZBUKjt8ZjYH")] pub type BankSlotDelta = SlotDelta>; type TransactionAccountRefCells = Vec>>; type TransactionAccountDepRefCells = Vec<(Pubkey, Rc>)>; diff --git a/sdk/program/src/instruction.rs b/sdk/program/src/instruction.rs index 1f4df4db0d..3cbc5926cf 100644 --- a/sdk/program/src/instruction.rs +++ b/sdk/program/src/instruction.rs @@ -214,6 +214,10 @@ pub enum InstructionError { /// Unsupported sysvar #[error("Unsupported sysvar")] UnsupportedSysvar, + + /// Illegal account owner + #[error("Provided owner is not allowed")] + IllegalOwner, } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index 15173e41bf..0941db372b 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -46,6 +46,27 @@ pub mod system_instruction; pub mod system_program; pub mod sysvar; +pub mod config { + pub mod program { + crate::declare_id!("Config1111111111111111111111111111111111111"); + } +} + +pub mod vote { + pub mod program { + crate::declare_id!("Vote111111111111111111111111111111111111111"); + } +} + +pub mod stake { + pub mod config { + crate::declare_id!("StakeConfig11111111111111111111111111111111"); + } + + pub mod program { + crate::declare_id!("Stake11111111111111111111111111111111111111"); + } +} /// Convenience macro to declare a static public key and functions to interact with it /// /// Input: a single literal base58 string representation of a program's id diff --git a/sdk/program/src/program_error.rs b/sdk/program/src/program_error.rs index 6de37645a0..76ff5612e3 100644 --- a/sdk/program/src/program_error.rs +++ b/sdk/program/src/program_error.rs @@ -44,6 +44,8 @@ pub enum ProgramError { AccountNotRentExempt, #[error("Unsupported sysvar")] UnsupportedSysvar, + #[error("Provided owner is not allowed")] + IllegalOwner, } pub trait PrintProgramError { @@ -81,6 +83,7 @@ impl PrintProgramError for ProgramError { Self::BorshIoError(_) => msg!("Error: BorshIoError"), Self::AccountNotRentExempt => msg!("Error: AccountNotRentExempt"), Self::UnsupportedSysvar => msg!("Error: UnsupportedSysvar"), + Self::IllegalOwner => msg!("Error: IllegalOwner"), } } } @@ -110,6 +113,7 @@ pub const INVALID_SEEDS: u64 = to_builtin!(14); pub const BORSH_IO_ERROR: u64 = to_builtin!(15); pub const ACCOUNT_NOT_RENT_EXEMPT: u64 = to_builtin!(16); pub const UNSUPPORTED_SYSVAR: u64 = to_builtin!(17); +pub const ILLEGAL_OWNER: u64 = to_builtin!(18); impl From for u64 { fn from(error: ProgramError) -> Self { @@ -130,6 +134,7 @@ impl From for u64 { ProgramError::BorshIoError(_) => BORSH_IO_ERROR, ProgramError::AccountNotRentExempt => ACCOUNT_NOT_RENT_EXEMPT, ProgramError::UnsupportedSysvar => UNSUPPORTED_SYSVAR, + ProgramError::IllegalOwner => ILLEGAL_OWNER, ProgramError::Custom(error) => { if error == 0 { @@ -160,6 +165,7 @@ impl From for ProgramError { INVALID_SEEDS => ProgramError::InvalidSeeds, UNSUPPORTED_SYSVAR => ProgramError::UnsupportedSysvar, CUSTOM_ZERO => ProgramError::Custom(0), + ILLEGAL_OWNER => ProgramError::IllegalOwner, _ => ProgramError::Custom(error as u32), } } @@ -186,6 +192,7 @@ impl TryFrom for ProgramError { Self::Error::BorshIoError(err) => Ok(Self::BorshIoError(err)), Self::Error::AccountNotRentExempt => Ok(Self::AccountNotRentExempt), Self::Error::UnsupportedSysvar => Ok(Self::UnsupportedSysvar), + Self::Error::IllegalOwner => Ok(Self::IllegalOwner), _ => Err(error), } } @@ -213,6 +220,7 @@ where MAX_SEED_LENGTH_EXCEEDED => InstructionError::MaxSeedLengthExceeded, INVALID_SEEDS => InstructionError::InvalidSeeds, UNSUPPORTED_SYSVAR => InstructionError::UnsupportedSysvar, + ILLEGAL_OWNER => InstructionError::IllegalOwner, _ => { // A valid custom error has no bits set in the upper 32 if error >> BUILTIN_BIT_SHIFT == 0 { @@ -230,6 +238,7 @@ impl From for ProgramError { match error { PubkeyError::MaxSeedLengthExceeded => ProgramError::MaxSeedLengthExceeded, PubkeyError::InvalidSeeds => ProgramError::InvalidSeeds, + PubkeyError::IllegalOwner => ProgramError::IllegalOwner, } } } diff --git a/sdk/program/src/pubkey.rs b/sdk/program/src/pubkey.rs index cc60a65f1b..ecda337304 100644 --- a/sdk/program/src/pubkey.rs +++ b/sdk/program/src/pubkey.rs @@ -1,4 +1,8 @@ -use crate::{decode_error::DecodeError, hash::hashv}; +use crate::{ + bpf_loader, bpf_loader_deprecated, config, decode_error::DecodeError, feature, hash::hashv, + secp256k1_program, stake, system_program, sysvar, vote, +}; + use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use num_derive::{FromPrimitive, ToPrimitive}; use std::{ @@ -17,6 +21,8 @@ pub const MAX_SEEDS: usize = 16; /// Maximum string length of a base58 encoded pubkey const MAX_BASE58_LEN: usize = 44; +const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress"; + #[derive(Error, Debug, Serialize, Clone, PartialEq, FromPrimitive, ToPrimitive)] pub enum PubkeyError { /// Length of the seed is too long for address generation @@ -24,6 +30,8 @@ pub enum PubkeyError { MaxSeedLengthExceeded, #[error("Provided seeds do not result in a valid address")] InvalidSeeds, + #[error("Provided owner is not allowed")] + IllegalOwner, } impl DecodeError for PubkeyError { fn type_of() -> &'static str { @@ -158,8 +166,16 @@ impl Pubkey { return Err(PubkeyError::MaxSeedLengthExceeded); } + let owner = owner.as_ref(); + if owner.len() >= PDA_MARKER.len() { + let slice = &owner[owner.len() - PDA_MARKER.len()..]; + if slice == PDA_MARKER { + return Err(PubkeyError::IllegalOwner); + } + } + Ok(Pubkey::new( - hashv(&[base.as_ref(), seed.as_ref(), owner.as_ref()]).as_ref(), + hashv(&[base.as_ref(), seed.as_ref(), owner]).as_ref(), )) } @@ -199,6 +215,10 @@ impl Pubkey { } } + if program_id.is_native_program_id() { + return Err(PubkeyError::IllegalOwner); + } + // Perform the calculation inline, calling this from within a program is // not supported #[cfg(not(target_arch = "bpf"))] @@ -207,7 +227,7 @@ impl Pubkey { for seed in seeds.iter() { hasher.hash(seed); } - hasher.hashv(&[program_id.as_ref(), "ProgramDerivedAddress".as_ref()]); + hasher.hashv(&[program_id.as_ref(), PDA_MARKER]); let hash = hasher.result(); if bytes_are_curve_point(hash) { @@ -288,9 +308,10 @@ impl Pubkey { { let mut seeds_with_bump = seeds.to_vec(); seeds_with_bump.push(&bump_seed); - if let Ok(address) = Self::create_program_address(&seeds_with_bump, program_id) - { - return Some((address, bump_seed[0])); + match Self::create_program_address(&seeds_with_bump, program_id) { + Ok(address) => return Some((address, bump_seed[0])), + Err(PubkeyError::InvalidSeeds) => (), + _ => break, } } bump_seed[0] -= 1; @@ -348,6 +369,22 @@ impl Pubkey { #[cfg(not(target_arch = "bpf"))] crate::program_stubs::sol_log(&self.to_string()); } + + pub fn is_native_program_id(&self) -> bool { + let all_program_ids = [ + bpf_loader::id(), + bpf_loader_deprecated::id(), + feature::id(), + config::program::id(), + stake::program::id(), + stake::config::id(), + vote::program::id(), + secp256k1_program::id(), + system_program::id(), + sysvar::id(), + ]; + all_program_ids.contains(self) + } } impl AsRef<[u8]> for Pubkey { @@ -478,7 +515,7 @@ mod tests { fn test_create_program_address() { let exceeded_seed = &[127; MAX_SEED_LEN + 1]; let max_seed = &[0; MAX_SEED_LEN]; - let program_id = Pubkey::from_str("BPFLoader1111111111111111111111111111111111").unwrap(); + let program_id = Pubkey::from_str("BPFLoaderUpgradeab1e11111111111111111111111").unwrap(); let public_key = Pubkey::from_str("SeedPubey1111111111111111111111111111111111").unwrap(); assert_eq!( @@ -492,25 +529,25 @@ mod tests { assert!(Pubkey::create_program_address(&[max_seed], &program_id).is_ok()); assert_eq!( Pubkey::create_program_address(&[b"", &[1]], &program_id), - Ok("3gF2KMe9KiC6FNVBmfg9i267aMPvK37FewCip4eGBFcT" + Ok("BwqrghZA2htAcqq8dzP1WDAhTXYTYWj7CHxF5j7TDBAe" .parse() .unwrap()) ); assert_eq!( - Pubkey::create_program_address(&["☉".as_ref()], &program_id), - Ok("7ytmC1nT1xY4RfxCV2ZgyA7UakC93do5ZdyhdF3EtPj7" + Pubkey::create_program_address(&["☉".as_ref(), &[0]], &program_id), + Ok("13yWmRpaTR4r5nAktwLqMpRNr28tnVUZw26rTvPSSB19" .parse() .unwrap()) ); assert_eq!( Pubkey::create_program_address(&[b"Talking", b"Squirrels"], &program_id), - Ok("HwRVBufQ4haG5XSgpspwKtNd3PC9GM9m1196uJW36vds" + Ok("2fnQrngrQT4SeLcdToJAD96phoEjNL2man2kfRLCASVk" .parse() .unwrap()) ); assert_eq!( - Pubkey::create_program_address(&[public_key.as_ref()], &program_id), - Ok("GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K" + Pubkey::create_program_address(&[public_key.as_ref(), &[1]], &program_id), + Ok("976ymqVnfE32QFe6NfGDctSvVa36LWnvYxhU6G2232YL" .parse() .unwrap()) ); @@ -557,4 +594,18 @@ mod tests { ); } } + + #[test] + fn test_is_native_program_id() { + assert!(bpf_loader::id().is_native_program_id()); + assert!(bpf_loader_deprecated::id().is_native_program_id()); + assert!(config::program::id().is_native_program_id()); + assert!(feature::id().is_native_program_id()); + assert!(secp256k1_program::id().is_native_program_id()); + assert!(stake::program::id().is_native_program_id()); + assert!(stake::config::id().is_native_program_id()); + assert!(system_program::id().is_native_program_id()); + assert!(sysvar::id().is_native_program_id()); + assert!(vote::program::id().is_native_program_id()); + } } diff --git a/storage-proto/proto/solana.storage.transaction_by_addr.rs b/storage-proto/proto/solana.storage.transaction_by_addr.rs index 767320a28e..5425eaab4e 100644 --- a/storage-proto/proto/solana.storage.transaction_by_addr.rs +++ b/storage-proto/proto/solana.storage.transaction_by_addr.rs @@ -120,4 +120,5 @@ pub enum InstructionErrorType { InvalidAccountOwner = 46, ArithmeticOverflow = 47, UnsupportedSysvar = 48, + IllegalOwner = 49, } diff --git a/storage-proto/src/convert.rs b/storage-proto/src/convert.rs index 1cf7da5961..9ad5dd9b5c 100644 --- a/storage-proto/src/convert.rs +++ b/storage-proto/src/convert.rs @@ -753,6 +753,9 @@ impl From for tx_by_addr::TransactionError { InstructionError::UnsupportedSysvar => { tx_by_addr::InstructionErrorType::UnsupportedSysvar } + InstructionError::IllegalOwner => { + tx_by_addr::InstructionErrorType::IllegalOwner + } } as i32, custom: match instruction_error { InstructionError::Custom(custom) => {