Remove support for dynamically loaded native programs (#20444)

This commit is contained in:
Jack May 2021-10-06 14:53:23 -07:00 committed by GitHub
parent a98aefa14e
commit 785fcb63f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 16 additions and 503 deletions

29
Cargo.lock generated
View File

@ -4730,14 +4730,6 @@ dependencies = [
"solana-sdk",
]
[[package]]
name = "solana-failure-program"
version = "1.8.0"
dependencies = [
"solana-runtime",
"solana-sdk",
]
[[package]]
name = "solana-faucet"
version = "1.8.0"
@ -5163,15 +5155,6 @@ dependencies = [
"url 2.2.2",
]
[[package]]
name = "solana-noop-program"
version = "1.8.0"
dependencies = [
"log 0.4.14",
"solana-logger 1.8.0",
"solana-sdk",
]
[[package]]
name = "solana-notifier"
version = "1.8.0"
@ -5181,18 +5164,6 @@ dependencies = [
"serde_json",
]
[[package]]
name = "solana-ownable"
version = "1.8.0"
dependencies = [
"bincode",
"num-derive",
"num-traits",
"solana-runtime",
"solana-sdk",
"thiserror",
]
[[package]]
name = "solana-perf"
version = "1.8.0"

View File

@ -49,9 +49,6 @@ members = [
"programs/bpf_loader",
"programs/compute-budget",
"programs/config",
"programs/failure",
"programs/noop",
"programs/ownable",
"programs/stake",
"programs/vote",
"rbpf-cli",

View File

@ -4,7 +4,9 @@ use solana_sdk::{
account::{AccountSharedData, ReadableAccount, WritableAccount},
account_utils::StateMut,
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
feature_set::{demote_program_write_locks, do_support_realloc, fix_write_privs},
feature_set::{
demote_program_write_locks, do_support_realloc, fix_write_privs, remove_native_loader,
},
ic_msg,
instruction::{Instruction, InstructionError},
message::Message,
@ -364,12 +366,14 @@ impl InstructionProcessor {
return process_instruction(program_id, instruction_data, invoke_context);
}
}
// Call the program via the native loader
return self.native_loader.process_instruction(
&solana_sdk::native_loader::id(),
instruction_data,
invoke_context,
);
if !invoke_context.is_feature_active(&remove_native_loader::id()) {
// Call the program via the native loader
return self.native_loader.process_instruction(
&solana_sdk::native_loader::id(),
instruction_data,
invoke_context,
);
}
} else {
let owner_id = &root_account.owner()?;
for (id, process_instruction) in &self.programs {

View File

@ -1,24 +0,0 @@
[package]
name = "solana-failure-program"
version = "1.8.0"
description = "Solana failure program"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-failure-program"
edition = "2018"
[dependencies]
solana-sdk = { path = "../../sdk", version = "=1.8.0" }
[dev-dependencies]
solana-runtime = { path = "../../runtime", version = "=1.8.0" }
[lib]
crate-type = ["lib", "cdylib"]
name = "solana_failure_program"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,17 +0,0 @@
use solana_sdk::{
instruction::InstructionError, process_instruction::InvokeContext, pubkey::Pubkey,
};
solana_sdk::declare_program!(
"FaiLure111111111111111111111111111111111111",
solana_failure_program,
process_instruction
);
fn process_instruction(
_program_id: &Pubkey,
_data: &[u8],
_invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
Err(InstructionError::Custom(0))
}

View File

@ -1,27 +0,0 @@
use solana_runtime::bank::Bank;
use solana_runtime::bank_client::BankClient;
use solana_runtime::loader_utils::create_invoke_instruction;
use solana_sdk::client::SyncClient;
use solana_sdk::genesis_config::create_genesis_config;
use solana_sdk::instruction::InstructionError;
use solana_sdk::signature::Signer;
use solana_sdk::transaction::TransactionError;
#[test]
fn test_program_native_failure() {
let (genesis_config, alice_keypair) = create_genesis_config(50);
let program_id = solana_sdk::pubkey::new_rand();
let bank = Bank::new_for_tests(&genesis_config);
bank.add_builtin_account("solana_failure_program", &program_id, false);
// Call user program
let instruction = create_invoke_instruction(alice_keypair.pubkey(), program_id, &1u8);
let bank_client = BankClient::new(bank);
assert_eq!(
bank_client
.send_and_confirm_instruction(&alice_keypair, instruction)
.unwrap_err()
.unwrap(),
TransactionError::InstructionError(0, InstructionError::Custom(0))
);
}

View File

@ -1,23 +0,0 @@
[package]
name = "solana-noop-program"
version = "1.8.0"
description = "Solana Noop program"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-noop-program"
edition = "2018"
[dependencies]
log = "0.4.14"
solana-logger = { path = "../../logger", version = "=1.8.0" }
solana-sdk = { path = "../../sdk", version = "=1.8.0" }
[lib]
crate-type = ["lib", "cdylib"]
name = "solana_noop_program"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,21 +0,0 @@
use log::*;
use solana_sdk::{
instruction::InstructionError, process_instruction::InvokeContext, pubkey::Pubkey,
};
solana_sdk::declare_program!(
"Noop111111111111111111111111111111111111111",
solana_noop_program,
process_instruction
);
pub fn process_instruction(
program_id: &Pubkey,
data: &[u8],
_invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
solana_logger::setup();
trace!("noop: program_id: {:?}", program_id);
trace!("noop: data: {:?}", data);
Ok(())
}

View File

@ -1,27 +0,0 @@
[package]
name = "solana-ownable"
version = "1.8.0"
description = "ownable program"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
documentation = "https://docs.rs/solana-ownable"
edition = "2018"
[dependencies]
bincode = "1.3.3"
solana-sdk = { path = "../../sdk", version = "=1.8.0" }
num-derive = "0.3"
num-traits = "0.2"
thiserror = "1.0"
[dev-dependencies]
solana-runtime = { path = "../../runtime", version = "=1.8.0" }
[lib]
crate-type = ["lib", "cdylib"]
name = "solana_ownable"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

View File

@ -1,4 +0,0 @@
pub mod ownable_instruction;
pub mod ownable_processor;
solana_sdk::declare_id!("ownab1e111111111111111111111111111111111111");

View File

@ -1,52 +0,0 @@
use num_derive::{FromPrimitive, ToPrimitive};
use solana_sdk::{
decode_error::DecodeError,
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
system_instruction,
};
use thiserror::Error;
#[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)]
pub enum OwnableError {
#[error("incorrect error")]
IncorrectOwner,
}
impl<T> DecodeError<T> for OwnableError {
fn type_of() -> &'static str {
"OwnableError"
}
}
fn initialize_account(account_pubkey: &Pubkey, owner_pubkey: &Pubkey) -> Instruction {
let keys = vec![AccountMeta::new(*account_pubkey, false)];
Instruction::new_with_bincode(crate::id(), &owner_pubkey, keys)
}
pub fn create_account(
payer_pubkey: &Pubkey,
account_pubkey: &Pubkey,
owner_pubkey: &Pubkey,
lamports: u64,
) -> Vec<Instruction> {
let space = std::mem::size_of::<Pubkey>() as u64;
vec![
system_instruction::create_account(
payer_pubkey,
account_pubkey,
lamports,
space,
&crate::id(),
),
initialize_account(account_pubkey, owner_pubkey),
]
}
pub fn set_owner(account_pubkey: &Pubkey, old_pubkey: &Pubkey, new_pubkey: &Pubkey) -> Instruction {
let keys = vec![
AccountMeta::new(*account_pubkey, false),
AccountMeta::new(*old_pubkey, true),
];
Instruction::new_with_bincode(crate::id(), &new_pubkey, keys)
}

View File

@ -1,186 +0,0 @@
//! Ownable program
use crate::ownable_instruction::OwnableError;
use bincode::serialize_into;
use solana_sdk::{
account::{ReadableAccount, WritableAccount},
instruction::InstructionError,
keyed_account::{keyed_account_at_index, KeyedAccount},
process_instruction::InvokeContext,
program_utils::limited_deserialize,
pubkey::Pubkey,
};
fn set_owner(
account_owner_pubkey: &mut Pubkey,
new_owner_pubkey: Pubkey,
owner_keyed_account: &KeyedAccount,
) -> Result<(), InstructionError> {
match owner_keyed_account.signer_key() {
None => return Err(InstructionError::MissingRequiredSignature),
Some(signer_key) => {
if account_owner_pubkey != signer_key {
return Err(OwnableError::IncorrectOwner.into());
}
*account_owner_pubkey = new_owner_pubkey;
}
}
Ok(())
}
pub fn process_instruction(
_program_id: &Pubkey,
data: &[u8],
invoke_context: &mut dyn InvokeContext,
) -> Result<(), InstructionError> {
let keyed_accounts = invoke_context.get_keyed_accounts()?;
let new_owner_pubkey: Pubkey = limited_deserialize(data)?;
let account_keyed_account = &mut keyed_account_at_index(keyed_accounts, 0)?;
let mut account_owner_pubkey: Pubkey =
limited_deserialize(account_keyed_account.try_account_ref()?.data())?;
if account_owner_pubkey == Pubkey::default() {
account_owner_pubkey = new_owner_pubkey;
} else {
let owner_keyed_account = &mut keyed_account_at_index(keyed_accounts, 1)?;
set_owner(
&mut account_owner_pubkey,
new_owner_pubkey,
owner_keyed_account,
)?;
}
let mut account = account_keyed_account.try_account_ref_mut()?;
serialize_into(account.data_as_mut_slice(), &account_owner_pubkey)
.map_err(|_| InstructionError::AccountDataTooSmall)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ownable_instruction;
use solana_runtime::{bank::Bank, bank_client::BankClient};
use solana_sdk::{
account::AccountSharedData,
client::SyncClient,
genesis_config::create_genesis_config,
message::Message,
signature::{Keypair, Signature, Signer},
system_program,
transport::Result,
};
fn create_bank(lamports: u64) -> (Bank, Keypair) {
let (genesis_config, mint_keypair) = create_genesis_config(lamports);
let mut bank = Bank::new_for_tests(&genesis_config);
bank.add_builtin("ownable_program", &crate::id(), process_instruction);
(bank, mint_keypair)
}
fn create_bank_client(lamports: u64) -> (BankClient, Keypair) {
let (bank, mint_keypair) = create_bank(lamports);
(BankClient::new(bank), mint_keypair)
}
fn create_ownable_account(
bank_client: &BankClient,
payer_keypair: &Keypair,
account_keypair: &Keypair,
owner_pubkey: &Pubkey,
lamports: u64,
) -> Result<Signature> {
let instructions = ownable_instruction::create_account(
&payer_keypair.pubkey(),
&account_keypair.pubkey(),
owner_pubkey,
lamports,
);
let message = Message::new(&instructions, Some(&payer_keypair.pubkey()));
bank_client.send_and_confirm_message(&[payer_keypair, account_keypair], message)
}
fn send_set_owner(
bank_client: &BankClient,
payer_keypair: &Keypair,
account_pubkey: &Pubkey,
old_owner_keypair: &Keypair,
new_owner_pubkey: &Pubkey,
) -> Result<Signature> {
let instruction = ownable_instruction::set_owner(
account_pubkey,
&old_owner_keypair.pubkey(),
new_owner_pubkey,
);
let message = Message::new(&[instruction], Some(&payer_keypair.pubkey()));
bank_client.send_and_confirm_message(&[payer_keypair, old_owner_keypair], message)
}
#[test]
fn test_ownable_set_owner() {
let (bank_client, payer_keypair) = create_bank_client(2);
let account_keypair = Keypair::new();
let account_pubkey = account_keypair.pubkey();
let owner_keypair = Keypair::new();
let owner_pubkey = owner_keypair.pubkey();
create_ownable_account(
&bank_client,
&payer_keypair,
&account_keypair,
&owner_pubkey,
1,
)
.unwrap();
let new_owner_keypair = Keypair::new();
let new_owner_pubkey = new_owner_keypair.pubkey();
send_set_owner(
&bank_client,
&payer_keypair,
&account_pubkey,
&owner_keypair,
&new_owner_pubkey,
)
.unwrap();
let account_data = bank_client
.get_account_data(&account_pubkey)
.unwrap()
.unwrap();
let account_owner_pubkey: Pubkey = limited_deserialize(&account_data).unwrap();
assert_eq!(account_owner_pubkey, new_owner_pubkey);
}
#[test]
fn test_ownable_missing_owner_signature() {
let mut account_owner_pubkey = solana_sdk::pubkey::new_rand();
let owner_pubkey = account_owner_pubkey;
let new_owner_pubkey = solana_sdk::pubkey::new_rand();
let account = AccountSharedData::new_ref(1, 0, &system_program::id());
let owner_keyed_account = KeyedAccount::new(&owner_pubkey, false, &account); // <-- Attack! Setting owner without the original owner's signature.
let err = set_owner(
&mut account_owner_pubkey,
new_owner_pubkey,
&owner_keyed_account,
)
.unwrap_err();
assert_eq!(err, InstructionError::MissingRequiredSignature);
}
#[test]
fn test_ownable_incorrect_owner() {
let mut account_owner_pubkey = solana_sdk::pubkey::new_rand();
let new_owner_pubkey = solana_sdk::pubkey::new_rand();
let account = AccountSharedData::new_ref(1, 0, &system_program::id());
let mallory_pubkey = solana_sdk::pubkey::new_rand(); // <-- Attack! Signing with wrong pubkey
let owner_keyed_account = KeyedAccount::new(&mallory_pubkey, true, &account);
let err = set_owner(
&mut account_owner_pubkey,
new_owner_pubkey,
&owner_keyed_account,
)
.unwrap_err();
assert_eq!(err, OwnableError::IncorrectOwner.into());
}
}

View File

@ -6293,7 +6293,6 @@ pub(crate) mod tests {
status_cache::MAX_CACHE_ENTRIES,
};
use crossbeam_channel::{bounded, unbounded};
use solana_program_runtime::NativeLoaderError;
#[allow(deprecated)]
use solana_sdk::sysvar::fees::Fees;
use solana_sdk::{
@ -13420,67 +13419,6 @@ pub(crate) mod tests {
);
}
#[test]
fn test_bad_native_loader() {
let (genesis_config, mint_keypair) = create_genesis_config(50000);
let bank = Bank::new_for_tests(&genesis_config);
let to_keypair = Keypair::new();
let tx = Transaction::new_signed_with_payer(
&[
system_instruction::create_account(
&mint_keypair.pubkey(),
&to_keypair.pubkey(),
10000,
0,
&native_loader::id(),
),
Instruction::new_with_bincode(
native_loader::id(),
&(),
vec![AccountMeta::new(to_keypair.pubkey(), false)],
),
],
Some(&mint_keypair.pubkey()),
&[&mint_keypair, &to_keypair],
bank.last_blockhash(),
);
assert_eq!(
bank.process_transaction(&tx),
Err(TransactionError::InstructionError(
1,
InstructionError::Custom(NativeLoaderError::InvalidAccountData as u32)
))
);
let tx = Transaction::new_signed_with_payer(
&[
system_instruction::create_account(
&mint_keypair.pubkey(),
&to_keypair.pubkey(),
10000,
100,
&native_loader::id(),
),
Instruction::new_with_bincode(
native_loader::id(),
&(),
vec![AccountMeta::new(to_keypair.pubkey(), false)],
),
],
Some(&mint_keypair.pubkey()),
&[&mint_keypair, &to_keypair],
bank.last_blockhash(),
);
assert_eq!(
bank.process_transaction(&tx),
Err(TransactionError::InstructionError(
1,
InstructionError::Custom(NativeLoaderError::InvalidAccountData as u32)
))
);
}
#[test]
fn test_debug_bank() {
let (genesis_config, _mint_keypair) = create_genesis_config(50000);

View File

@ -1,21 +0,0 @@
use solana_runtime::{
bank::Bank, bank_client::BankClient, loader_utils::create_invoke_instruction,
};
use solana_sdk::{client::SyncClient, genesis_config::create_genesis_config, signature::Signer};
#[test]
fn test_program_native_noop() {
solana_logger::setup();
let (genesis_config, alice_keypair) = create_genesis_config(50);
let program_id = solana_sdk::pubkey::new_rand();
let bank = Bank::new_for_tests(&genesis_config);
bank.add_builtin_account("solana_noop_program", &program_id, false);
// Call user program
let instruction = create_invoke_instruction(alice_keypair.pubkey(), program_id, &1u8);
let bank_client = BankClient::new(bank);
bank_client
.send_and_confirm_instruction(&alice_keypair, instruction)
.unwrap();
}

View File

@ -233,6 +233,10 @@ pub mod optimize_epoch_boundary_updates {
solana_sdk::declare_id!("265hPS8k8xJ37ot82KEgjRunsUp5w4n4Q4VwwiN9i9ps");
}
pub mod remove_native_loader {
solana_sdk::declare_id!("HTTgmruMYRZEntyL3EdCDdnS6e4D5wRq1FA7kQsb66qq");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -286,6 +290,7 @@ lazy_static! {
(do_support_realloc::id(), "support account data reallocation"),
(prevent_calling_precompiles_as_programs::id(), "Prevent calling precompiles as programs"),
(optimize_epoch_boundary_updates::id(), "Optimize epoch boundary updates"),
(remove_native_loader::id(), "Remove support for the native loader"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()