diff --git a/programs/native/bpf_loader/src/lib.rs b/programs/native/bpf_loader/src/lib.rs index 26ddff488..b14cec661 100644 --- a/programs/native/bpf_loader/src/lib.rs +++ b/programs/native/bpf_loader/src/lib.rs @@ -197,21 +197,13 @@ pub extern "C" fn process(keyed_accounts: &mut [KeyedAccount], tx_data: &[u8]) - .userdata .splice(0..s.len(), s.iter().cloned()); } - LoaderInstruction::Finalize => { keyed_accounts[0].account.executable = true; - keyed_accounts[0].account.loader_program_id = keyed_accounts[0].account.program_id; - keyed_accounts[0].account.program_id = *keyed_accounts[0].key; - trace!( - "BPFLoader::Finalize prog: {:?} loader {:?}", - keyed_accounts[0].account.program_id, - keyed_accounts[0].account.loader_program_id - ); + trace!("BPfLoader::Finalize prog: {:?}", keyed_accounts[0].key); } } } else { warn!("Invalid program transaction: {:?}", tx_data); - return false; } true } diff --git a/programs/native/lua_loader/src/lib.rs b/programs/native/lua_loader/src/lib.rs index a6a899787..c8a6a3c46 100644 --- a/programs/native/lua_loader/src/lib.rs +++ b/programs/native/lua_loader/src/lib.rs @@ -114,13 +114,7 @@ pub extern "C" fn process(keyed_accounts: &mut [KeyedAccount], tx_data: &[u8]) - LoaderInstruction::Finalize => { keyed_accounts[0].account.executable = true; - keyed_accounts[0].account.loader_program_id = keyed_accounts[0].account.program_id; - keyed_accounts[0].account.program_id = *keyed_accounts[0].key; - trace!( - "LuaLoader::Finalize prog: {:?} loader {:?}", - keyed_accounts[0].account.program_id, - keyed_accounts[0].account.loader_program_id - ); + trace!("LuaLoader::Finalize prog: {:?}", keyed_accounts[0].key); } } } else { diff --git a/src/bank.rs b/src/bank.rs index 5f9321be5..02c17f86a 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -486,13 +486,10 @@ impl Bank { account: &Account, ) -> Result<()> { // Verify the transaction - // make sure that program_id is still the same or this was just assigned by the system call contract - if !((*pre_program_id == account.program_id) - || (SystemProgram::check_id(&tx_program_id) - && SystemProgram::check_id(&pre_program_id))) - { - //TODO, this maybe redundant bpf should be able to guarantee this property - // return Err(BankError::ModifiedContractId(instruction_index as u8)); + + // Make sure that program_id is still the same or this was just assigned by the system call contract + if *pre_program_id != account.program_id && !SystemProgram::check_id(&tx_program_id) { + return Err(BankError::ModifiedContractId(instruction_index as u8)); } // For accounts unassigned to the contract, the individual balance of each accounts cannot decrease. if *tx_program_id != account.program_id && pre_tokens > account.tokens { diff --git a/src/native_loader.rs b/src/native_loader.rs index 71d57a20d..6ed0e88db 100644 --- a/src/native_loader.rs +++ b/src/native_loader.rs @@ -65,21 +65,18 @@ pub fn process_transaction(keyed_accounts: &mut [KeyedAccount], tx_data: &[u8]) } }; trace!("Call native {:?}", name); - { - // create native program - let path = create_path(&name); - // TODO linux tls bug can cause crash on dlclose(), workaround by never unloading - let library = Library::open(Some(path), libc::RTLD_NODELETE | libc::RTLD_NOW).unwrap(); - unsafe { - let entrypoint: Symbol = match library.get(ENTRYPOINT.as_bytes()) { - Ok(s) => s, - Err(e) => { - warn!("{:?}: Unable to find {:?} in program", e, ENTRYPOINT); - return false; - } - }; - return entrypoint(&mut keyed_accounts[1..], tx_data); - } + let path = create_path(&name); + // TODO linux tls bug can cause crash on dlclose(), workaround by never unloading + let library = Library::open(Some(path), libc::RTLD_NODELETE | libc::RTLD_NOW).unwrap(); + unsafe { + let entrypoint: Symbol = match library.get(ENTRYPOINT.as_bytes()) { + Ok(s) => s, + Err(e) => { + warn!("{:?}: Unable to find {:?} in program", e, ENTRYPOINT); + return false; + } + }; + return entrypoint(&mut keyed_accounts[1..], tx_data); } } else if let Ok(instruction) = deserialize(tx_data) { match instruction { @@ -96,23 +93,15 @@ pub fn process_transaction(keyed_accounts: &mut [KeyedAccount], tx_data: &[u8]) } // native loader takes a name and we assume it all comes in at once keyed_accounts[0].account.userdata = bytes; - return true; } LoaderInstruction::Finalize => { keyed_accounts[0].account.executable = true; - keyed_accounts[0].account.loader_program_id = id(); - keyed_accounts[0].account.program_id = *keyed_accounts[0].key; - trace!( - "NativeLoader::Finalize prog: {:?} loader {:?}", - keyed_accounts[0].account.program_id, - keyed_accounts[0].account.loader_program_id - ); - return true; + trace!("NativeLoader::Finalize prog: {:?}", keyed_accounts[0].key); } } } else { warn!("Invalid program transaction: {:?}", tx_data); } - false + true } diff --git a/src/system_program.rs b/src/system_program.rs index 6667bf1f4..3e194d37d 100644 --- a/src/system_program.rs +++ b/src/system_program.rs @@ -9,6 +9,8 @@ use transaction::Transaction; #[derive(Debug)] pub enum Error { InvalidArgument, + AssignOfUnownedAccount, + AccountNotFinalized, } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -39,6 +41,9 @@ pub enum SystemProgram { /// * Transaction::keys[0] - source /// * Transaction::keys[1] - destination Move { tokens: i64 }, + + /// Spawn a new program from an account + Spawn, } pub const SYSTEM_PROGRAM_ID: [u8; 32] = [0u8; 32]; @@ -85,7 +90,7 @@ impl SystemProgram { } SystemProgram::Assign { program_id } => { if !Self::check_id(&accounts[0].program_id) { - Err(Error::InvalidArgument)?; + Err(Error::AssignOfUnownedAccount)?; } accounts[0].program_id = program_id; } @@ -94,6 +99,14 @@ impl SystemProgram { accounts[0].tokens -= tokens; accounts[1].tokens += tokens; } + SystemProgram::Spawn => { + if !accounts[0].executable || accounts[0].loader_program_id != Pubkey::default() + { + Err(Error::AccountNotFinalized)?; + } + accounts[0].loader_program_id = accounts[0].program_id; + accounts[0].program_id = tx.account_keys[0]; + } } Ok(()) } else { diff --git a/src/system_transaction.rs b/src/system_transaction.rs index 993eb7c26..e28615d5b 100644 --- a/src/system_transaction.rs +++ b/src/system_transaction.rs @@ -36,6 +36,8 @@ pub trait SystemTransaction { last_id: Hash, fee: i64, ) -> Self; + + fn system_spawn(from_keypair: &Keypair, last_id: Hash, fee: i64) -> Self; } impl SystemTransaction for Transaction { @@ -100,7 +102,7 @@ impl SystemTransaction for Transaction { fee, ) } - + /// Create and sign new SystemProgram::Move transaction to many destinations fn system_move_many(from: &Keypair, moves: &[(Pubkey, i64)], last_id: Hash, fee: i64) -> Self { let instructions: Vec<_> = moves .iter() @@ -124,6 +126,19 @@ impl SystemTransaction for Transaction { instructions, ) } + /// Create and sign new SystemProgram::Spawn transaction + fn system_spawn(from_keypair: &Keypair, last_id: Hash, fee: i64) -> Self { + let spawn = SystemProgram::Spawn; + let userdata = serialize(&spawn).unwrap(); + Transaction::new( + from_keypair, + &[], + SystemProgram::id(), + userdata, + last_id, + fee, + ) + } } pub fn test_tx() -> Transaction { diff --git a/tests/programs.rs b/tests/programs.rs index f6cde253a..f2ad3448f 100644 --- a/tests/programs.rs +++ b/tests/programs.rs @@ -61,6 +61,9 @@ fn test_transaction_load_native() { let tx = Transaction::finalize(&program, native_loader::id(), mint.last_id(), 0); check_tx_results(&bank, &tx, bank.process_transactions(&vec![tx.clone()])); + let tx = Transaction::system_spawn(&program, mint.last_id(), 0); + check_tx_results(&bank, &tx, bank.process_transactions(&vec![tx.clone()])); + // Call user program let tx = Transaction::new( @@ -114,6 +117,9 @@ fn test_transaction_load_lua() { let tx = Transaction::finalize(&loader, native_loader::id(), mint.last_id(), 0); check_tx_results(&bank, &tx, bank.process_transactions(&vec![tx.clone()])); + let tx = Transaction::system_spawn(&loader, mint.last_id(), 0); + check_tx_results(&bank, &tx, bank.process_transactions(&vec![tx.clone()])); + // allocate, populate, and finalize user program let bytes = r#" @@ -141,6 +147,9 @@ fn test_transaction_load_lua() { let tx = Transaction::finalize(&program, loader.pubkey(), mint.last_id(), 0); check_tx_results(&bank, &tx, bank.process_transactions(&vec![tx.clone()])); + let tx = Transaction::system_spawn(&program, mint.last_id(), 0); + check_tx_results(&bank, &tx, bank.process_transactions(&vec![tx.clone()])); + // Call user program with two accounts let tx = Transaction::system_create( @@ -211,6 +220,9 @@ fn test_transaction_load_bpf() { let tx = Transaction::finalize(&loader, native_loader::id(), mint.last_id(), 0); check_tx_results(&bank, &tx, bank.process_transactions(&vec![tx.clone()])); + let tx = Transaction::system_spawn(&loader, mint.last_id(), 0); + check_tx_results(&bank, &tx, bank.process_transactions(&vec![tx.clone()])); + // allocate, populate, and finalize user program let tx = Transaction::system_create( @@ -238,6 +250,9 @@ fn test_transaction_load_bpf() { let tx = Transaction::finalize(&program, loader.pubkey(), mint.last_id(), 0); check_tx_results(&bank, &tx, bank.process_transactions(&vec![tx.clone()])); + let tx = Transaction::system_spawn(&program, mint.last_id(), 0); + check_tx_results(&bank, &tx, bank.process_transactions(&vec![tx.clone()])); + // Call user program let tx = Transaction::new(