diff --git a/programs/bpf/rust/invoke/src/lib.rs b/programs/bpf/rust/invoke/src/lib.rs index 2d715f93d..dec99fd88 100644 --- a/programs/bpf/rust/invoke/src/lib.rs +++ b/programs/bpf/rust/invoke/src/lib.rs @@ -263,8 +263,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 ); @@ -276,8 +278,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 a36369189..1cbeed057 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -2192,8 +2192,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()); let rent_id = sysvar::rent::id(); @@ -2846,8 +2848,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 e18b8e24e..5658232c3 100644 --- a/programs/config/src/lib.rs +++ b/programs/config/src/lib.rs @@ -12,7 +12,7 @@ use solana_sdk::{ stake::config::Config as StakeConfig, }; -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/vote/src/lib.rs b/programs/vote/src/lib.rs index ad9cf9f89..3995bbab8 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 cd9087816..eb50d48f8 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -175,7 +175,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 64e493129..1ad9cfa9d 100644 --- a/sdk/program/src/instruction.rs +++ b/sdk/program/src/instruction.rs @@ -231,6 +231,10 @@ pub enum InstructionError { /// Unsupported sysvar #[error("Unsupported sysvar")] UnsupportedSysvar, + + /// Illegal account owner + #[error("Provided owner is not allowed")] + IllegalOwner, // Note: For any new error added here an equivilent ProgramError and it's // conversions must also be added } diff --git a/sdk/program/src/lib.rs b/sdk/program/src/lib.rs index bed762359..190a5d158 100644 --- a/sdk/program/src/lib.rs +++ b/sdk/program/src/lib.rs @@ -49,6 +49,18 @@ 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"); + } +} + /// 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 b72543ec8..f07936cc3 100644 --- a/sdk/program/src/program_error.rs +++ b/sdk/program/src/program_error.rs @@ -45,6 +45,8 @@ pub enum ProgramError { AccountNotRentExempt, #[error("Unsupported sysvar")] UnsupportedSysvar, + #[error("Provided owner is not allowed")] + IllegalOwner, } pub trait PrintProgramError { @@ -82,6 +84,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"), } } } @@ -111,6 +114,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); // Warning: Any new program errors added here must also be: // - Added to the below conversions // - Added as an equivilent to InstructionError @@ -136,6 +140,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 { CUSTOM_ZERO @@ -167,6 +172,7 @@ impl From for ProgramError { BORSH_IO_ERROR => Self::BorshIoError("Unkown".to_string()), ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt, UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar, + ILLEGAL_OWNER => Self::IllegalOwner, _ => Self::Custom(error as u32), } } @@ -194,6 +200,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), } } @@ -223,6 +230,7 @@ where BORSH_IO_ERROR => Self::BorshIoError("Unkown".to_string()), ACCOUNT_NOT_RENT_EXEMPT => Self::AccountNotRentExempt, UNSUPPORTED_SYSVAR => Self::UnsupportedSysvar, + ILLEGAL_OWNER => Self::IllegalOwner, _ => { // A valid custom error has no bits set in the upper 32 if error >> BUILTIN_BIT_SHIFT == 0 { @@ -240,6 +248,7 @@ impl From for ProgramError { match error { PubkeyError::MaxSeedLengthExceeded => Self::MaxSeedLengthExceeded, PubkeyError::InvalidSeeds => Self::InvalidSeeds, + PubkeyError::IllegalOwner => Self::IllegalOwner, } } } diff --git a/sdk/program/src/pubkey.rs b/sdk/program/src/pubkey.rs index adb1d0544..91ede020d 100644 --- a/sdk/program/src/pubkey.rs +++ b/sdk/program/src/pubkey.rs @@ -1,5 +1,9 @@ #![allow(clippy::integer_arithmetic)] -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::{ @@ -18,6 +22,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 @@ -25,6 +31,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 { @@ -157,8 +165,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(), )) } @@ -198,6 +214,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"))] @@ -206,7 +226,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) { @@ -287,9 +307,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; @@ -347,6 +368,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 { @@ -483,7 +520,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!( @@ -497,25 +534,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()) ); @@ -562,4 +599,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 767320a28..5425eaab4 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 1cf7da596..9ad5dd9b5 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) => {