Limit deserialization of program inputs (#6522)

This commit is contained in:
Jack May 2019-10-23 19:56:07 -07:00 committed by GitHub
parent 955d0ab76f
commit ddefc96433
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 42 additions and 52 deletions

View File

@ -18,6 +18,7 @@ use log::*;
use solana_rbpf::{memory_region::MemoryRegion, EbpfVm}; use solana_rbpf::{memory_region::MemoryRegion, EbpfVm};
use solana_sdk::account::KeyedAccount; use solana_sdk::account::KeyedAccount;
use solana_sdk::instruction::InstructionError; use solana_sdk::instruction::InstructionError;
use solana_sdk::instruction_processor_utils::limited_deserialize;
use solana_sdk::loader_instruction::LoaderInstruction; use solana_sdk::loader_instruction::LoaderInstruction;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::sysvar::rent; use solana_sdk::sysvar::rent;
@ -89,7 +90,7 @@ pub fn process_instruction(
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
solana_logger::setup(); solana_logger::setup();
if let Ok(instruction) = bincode::deserialize(ix_data) { if let Ok(instruction) = limited_deserialize(ix_data) {
match instruction { match instruction {
LoaderInstruction::Write { offset, bytes } => { LoaderInstruction::Write { offset, bytes } => {
if keyed_accounts[0].signer_key().is_none() { if keyed_accounts[0].signer_key().is_none() {

View File

@ -8,6 +8,7 @@ use hex;
use log::*; use log::*;
use solana_sdk::account::KeyedAccount; use solana_sdk::account::KeyedAccount;
use solana_sdk::instruction::InstructionError; use solana_sdk::instruction::InstructionError;
use solana_sdk::instruction_processor_utils::limited_deserialize;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
pub struct SpvProcessor {} pub struct SpvProcessor {}
@ -103,10 +104,7 @@ pub fn process_instruction(
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
// solana_logger::setup(); // solana_logger::setup();
let command = bincode::deserialize::<SpvInstruction>(data).map_err(|err| { let command = limited_deserialize::<SpvInstruction>(data)?;
info!("invalid instruction data: {:?} {:?}", data, err);
InstructionError::InvalidInstructionData
})?;
trace!("{:?}", command); trace!("{:?}", command);

View File

@ -4,11 +4,11 @@ use crate::{
budget_instruction::{BudgetError, BudgetInstruction}, budget_instruction::{BudgetError, BudgetInstruction},
budget_state::BudgetState, budget_state::BudgetState,
}; };
use bincode::deserialize;
use chrono::prelude::{DateTime, Utc}; use chrono::prelude::{DateTime, Utc};
use log::*; use log::*;
use solana_sdk::{ use solana_sdk::{
account::KeyedAccount, hash::hash, instruction::InstructionError, pubkey::Pubkey, account::KeyedAccount, hash::hash, instruction::InstructionError,
instruction_processor_utils::limited_deserialize, pubkey::Pubkey,
}; };
/// Process a Witness Signature. Any payment plans waiting on this signature /// Process a Witness Signature. Any payment plans waiting on this signature
@ -106,10 +106,7 @@ pub fn process_instruction(
keyed_accounts: &mut [KeyedAccount], keyed_accounts: &mut [KeyedAccount],
data: &[u8], data: &[u8],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let instruction = deserialize(data).map_err(|err| { let instruction = limited_deserialize(data)?;
info!("Invalid transaction data: {:?} {:?}", data, err);
InstructionError::InvalidInstructionData
})?;
trace!("process_instruction: {:?}", instruction); trace!("process_instruction: {:?}", instruction);

View File

@ -5,7 +5,7 @@ use bincode::deserialize;
use log::*; use log::*;
use solana_sdk::account::KeyedAccount; use solana_sdk::account::KeyedAccount;
use solana_sdk::instruction::InstructionError; use solana_sdk::instruction::InstructionError;
use solana_sdk::instruction_processor_utils::next_keyed_account; use solana_sdk::instruction_processor_utils::{limited_deserialize, next_keyed_account};
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
pub fn process_instruction( pub fn process_instruction(
@ -13,11 +13,7 @@ pub fn process_instruction(
keyed_accounts: &mut [KeyedAccount], keyed_accounts: &mut [KeyedAccount],
data: &[u8], data: &[u8],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
let key_list: ConfigKeys = deserialize(data).map_err(|err| { let key_list: ConfigKeys = limited_deserialize(data)?;
error!("Invalid ConfigKeys data: {:?} {:?}", data, err);
InstructionError::InvalidInstructionData
})?;
let keyed_accounts_iter = &mut keyed_accounts.iter_mut(); let keyed_accounts_iter = &mut keyed_accounts.iter_mut();
let config_keyed_account = &mut next_keyed_account(keyed_accounts_iter)?; let config_keyed_account = &mut next_keyed_account(keyed_accounts_iter)?;
let current_data: ConfigKeys = let current_data: ConfigKeys =

View File

@ -7,6 +7,7 @@ use log::*;
use solana_metrics::inc_new_counter_info; use solana_metrics::inc_new_counter_info;
use solana_sdk::account::KeyedAccount; use solana_sdk::account::KeyedAccount;
use solana_sdk::instruction::InstructionError; use solana_sdk::instruction::InstructionError;
use solana_sdk::instruction_processor_utils::limited_deserialize;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use std::cmp; use std::cmp;
@ -432,14 +433,7 @@ pub fn process_instruction(
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
solana_logger::setup(); solana_logger::setup();
let command = bincode::deserialize::<ExchangeInstruction>(data).map_err(|err| { match limited_deserialize::<ExchangeInstruction>(data)? {
info!("Invalid transaction data: {:?} {:?}", data, err);
InstructionError::InvalidInstructionData
})?;
trace!("{:?}", command);
match command {
ExchangeInstruction::AccountRequest => { ExchangeInstruction::AccountRequest => {
ExchangeProcessor::do_account_request(keyed_accounts) ExchangeProcessor::do_account_request(keyed_accounts)
} }

View File

@ -6,7 +6,8 @@ use bytecode_verifier::{VerifiedModule, VerifiedScript};
use log::*; use log::*;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_sdk::{ use solana_sdk::{
account::KeyedAccount, instruction::InstructionError, loader_instruction::LoaderInstruction, account::KeyedAccount, instruction::InstructionError,
instruction_processor_utils::limited_deserialize, loader_instruction::LoaderInstruction,
pubkey::Pubkey, sysvar::rent, pubkey::Pubkey, sysvar::rent,
}; };
use types::{ use types::{
@ -36,14 +37,7 @@ pub fn process_instruction(
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
solana_logger::setup(); solana_logger::setup();
let command = bincode::deserialize::<LoaderInstruction>(data).map_err(|err| { match limited_deserialize(data)? {
info!("Invalid instruction: {:?} {:?}", data, err);
InstructionError::InvalidInstructionData
})?;
trace!("{:?}", command);
match command {
LoaderInstruction::Write { offset, bytes } => { LoaderInstruction::Write { offset, bytes } => {
MoveProcessor::do_write(keyed_accounts, offset, &bytes) MoveProcessor::do_write(keyed_accounts, offset, &bytes)
} }
@ -138,7 +132,7 @@ impl MoveProcessor {
fn deserialize_compiled_program( fn deserialize_compiled_program(
data: &[u8], data: &[u8],
) -> Result<(CompiledScript, Vec<CompiledModule>), InstructionError> { ) -> Result<(CompiledScript, Vec<CompiledModule>), InstructionError> {
match bincode::deserialize(data).map_err(map_data_error)? { match limited_deserialize(data)? {
LibraAccountState::CompiledProgram(string) => { LibraAccountState::CompiledProgram(string) => {
let program: Program = serde_json::from_str(&string).map_err(map_json_error)?; let program: Program = serde_json::from_str(&string).map_err(map_json_error)?;
@ -163,7 +157,7 @@ impl MoveProcessor {
fn deserialize_verified_program( fn deserialize_verified_program(
data: &[u8], data: &[u8],
) -> Result<(VerifiedScript, Vec<VerifiedModule>), InstructionError> { ) -> Result<(VerifiedScript, Vec<VerifiedModule>), InstructionError> {
match bincode::deserialize(data).map_err(map_data_error)? { match limited_deserialize(data)? {
LibraAccountState::VerifiedProgram { LibraAccountState::VerifiedProgram {
script_bytes, script_bytes,
modules_bytes, modules_bytes,
@ -234,7 +228,7 @@ impl MoveProcessor {
) -> Result<DataStore, InstructionError> { ) -> Result<DataStore, InstructionError> {
let mut data_store = DataStore::default(); let mut data_store = DataStore::default();
for keyed_account in keyed_accounts { for keyed_account in keyed_accounts {
match bincode::deserialize(&keyed_account.account.data).map_err(map_data_error)? { match limited_deserialize(&keyed_account.account.data)? {
LibraAccountState::Genesis(write_set) => data_store.apply_write_set(&write_set), LibraAccountState::Genesis(write_set) => data_store.apply_write_set(&write_set),
LibraAccountState::User(owner, write_set) => { LibraAccountState::User(owner, write_set) => {
if owner != *genesis_key { if owner != *genesis_key {
@ -349,7 +343,7 @@ impl MoveProcessor {
keyed_accounts: &mut [KeyedAccount], keyed_accounts: &mut [KeyedAccount],
data: &[u8], data: &[u8],
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
match bincode::deserialize(&data).map_err(map_data_error)? { match limited_deserialize(&data)? {
InvokeCommand::CreateGenesis(amount) => { InvokeCommand::CreateGenesis(amount) => {
if keyed_accounts.is_empty() { if keyed_accounts.is_empty() {
debug!("Error: Requires an unallocated account"); debug!("Error: Requires an unallocated account");
@ -360,9 +354,7 @@ impl MoveProcessor {
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
match bincode::deserialize(&keyed_accounts[0].account.data) match limited_deserialize(&keyed_accounts[0].account.data)? {
.map_err(map_data_error)?
{
LibraAccountState::Unallocated => Self::serialize_and_enforce_length( LibraAccountState::Unallocated => Self::serialize_and_enforce_length(
&LibraAccountState::create_genesis(amount)?, &LibraAccountState::create_genesis(amount)?,
&mut keyed_accounts[0].account.data, &mut keyed_accounts[0].account.data,

View File

@ -2,14 +2,13 @@ use crate::{
config, id, config, id,
stake_state::{Authorized, Lockup, StakeAccount, StakeAuthorize, StakeState}, stake_state::{Authorized, Lockup, StakeAccount, StakeAuthorize, StakeState},
}; };
use bincode::deserialize;
use log::*; use log::*;
use num_derive::{FromPrimitive, ToPrimitive}; use num_derive::{FromPrimitive, ToPrimitive};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use solana_sdk::{ use solana_sdk::{
account::{get_signers, KeyedAccount}, account::{get_signers, KeyedAccount},
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError},
instruction_processor_utils::{next_keyed_account, DecodeError}, instruction_processor_utils::{limited_deserialize, next_keyed_account, DecodeError},
pubkey::Pubkey, pubkey::Pubkey,
system_instruction, sysvar, system_instruction, sysvar,
sysvar::rent, sysvar::rent,
@ -275,7 +274,7 @@ pub fn process_instruction(
let me = &mut next_keyed_account(keyed_accounts)?; let me = &mut next_keyed_account(keyed_accounts)?;
// TODO: data-driven unpack and dispatch of KeyedAccounts // TODO: data-driven unpack and dispatch of KeyedAccounts
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? { match limited_deserialize(data)? {
StakeInstruction::Initialize(authorized, lockup) => { StakeInstruction::Initialize(authorized, lockup) => {
rent::verify_rent_exemption(me, next_keyed_account(keyed_accounts)?)?; rent::verify_rent_exemption(me, next_keyed_account(keyed_accounts)?)?;
me.initialize(&authorized, &lockup) me.initialize(&authorized, &lockup)

View File

@ -5,6 +5,7 @@ use crate::storage_contract::StorageAccount;
use crate::storage_instruction::StorageInstruction; use crate::storage_instruction::StorageInstruction;
use solana_sdk::account::KeyedAccount; use solana_sdk::account::KeyedAccount;
use solana_sdk::instruction::InstructionError; use solana_sdk::instruction::InstructionError;
use solana_sdk::instruction_processor_utils::limited_deserialize;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::sysvar; use solana_sdk::sysvar;
@ -19,7 +20,7 @@ pub fn process_instruction(
let me_unsigned = me[0].signer_key().is_none(); let me_unsigned = me[0].signer_key().is_none();
let mut storage_account = StorageAccount::new(*me[0].unsigned_key(), &mut me[0].account); let mut storage_account = StorageAccount::new(*me[0].unsigned_key(), &mut me[0].account);
match bincode::deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? { match limited_deserialize(data)? {
StorageInstruction::InitializeStorage { StorageInstruction::InitializeStorage {
owner, owner,
account_type, account_type,

View File

@ -9,7 +9,7 @@ homepage = "https://solana.com/"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
bincode = "1.1.4" bincode = "1.2.0"
chrono = { version = "0.4.9", features = ["serde"] } chrono = { version = "0.4.9", features = ["serde"] }
log = "0.4.8" log = "0.4.8"
num-derive = "0.2" num-derive = "0.2"

View File

@ -4,13 +4,12 @@ use crate::{
vest_instruction::{VestError, VestInstruction}, vest_instruction::{VestError, VestInstruction},
vest_state::VestState, vest_state::VestState,
}; };
use bincode::deserialize;
use chrono::prelude::*; use chrono::prelude::*;
use solana_config_api::get_config_data; use solana_config_api::get_config_data;
use solana_sdk::{ use solana_sdk::{
account::{Account, KeyedAccount}, account::{Account, KeyedAccount},
instruction::InstructionError, instruction::InstructionError,
instruction_processor_utils::next_keyed_account, instruction_processor_utils::{limited_deserialize, next_keyed_account},
pubkey::Pubkey, pubkey::Pubkey,
}; };
@ -62,7 +61,7 @@ pub fn process_instruction(
let keyed_accounts_iter = &mut keyed_accounts.iter_mut(); let keyed_accounts_iter = &mut keyed_accounts.iter_mut();
let contract_account = &mut next_keyed_account(keyed_accounts_iter)?.account; let contract_account = &mut next_keyed_account(keyed_accounts_iter)?.account;
let instruction = deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)?; let instruction = limited_deserialize(data)?;
let mut vest_state = if let VestInstruction::InitializeAccount { let mut vest_state = if let VestInstruction::InitializeAccount {
terminator_pubkey, terminator_pubkey,

View File

@ -5,7 +5,6 @@ use crate::{
id, id,
vote_state::{self, Vote, VoteAuthorize, VoteInit, VoteState}, vote_state::{self, Vote, VoteAuthorize, VoteInit, VoteState},
}; };
use bincode::deserialize;
use log::*; use log::*;
use num_derive::{FromPrimitive, ToPrimitive}; use num_derive::{FromPrimitive, ToPrimitive};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
@ -13,7 +12,7 @@ use solana_metrics::datapoint_debug;
use solana_sdk::{ use solana_sdk::{
account::KeyedAccount, account::KeyedAccount,
instruction::{AccountMeta, Instruction, InstructionError}, instruction::{AccountMeta, Instruction, InstructionError},
instruction_processor_utils::DecodeError, instruction_processor_utils::{limited_deserialize, DecodeError},
pubkey::Pubkey, pubkey::Pubkey,
system_instruction, system_instruction,
sysvar::{self, rent}, sysvar::{self, rent},
@ -178,7 +177,7 @@ pub fn process_instruction(
let me = &mut me[0]; let me = &mut me[0];
// TODO: data-driven unpack and dispatch of KeyedAccounts // TODO: data-driven unpack and dispatch of KeyedAccounts
match deserialize(data).map_err(|_| InstructionError::InvalidInstructionData)? { match limited_deserialize(data)? {
VoteInstruction::InitializeAccount(vote_init) => { VoteInstruction::InitializeAccount(vote_init) => {
if rest.is_empty() { if rest.is_empty() {
return Err(InstructionError::InvalidInstructionData); return Err(InstructionError::InvalidInstructionData);

View File

@ -41,6 +41,20 @@ pub fn next_keyed_account<I: Iterator>(iter: &mut I) -> Result<I::Item, Instruct
iter.next().ok_or(InstructionError::NotEnoughAccountKeys) iter.next().ok_or(InstructionError::NotEnoughAccountKeys)
} }
pub fn limited_deserialize<T>(data: &[u8]) -> Result<(T), InstructionError>
where
T: serde::de::DeserializeOwned,
{
#[cfg(not(feature = "program"))]
let limit = crate::packet::PACKET_DATA_SIZE as u64;
#[cfg(feature = "program")]
let limit = 1024;
bincode::config()
.limit(limit)
.deserialize(data)
.map_err(|_| InstructionError::InvalidInstructionData)
}
pub trait DecodeError<E> { pub trait DecodeError<E> {
fn decode_custom_error_to_enum(custom: u32) -> Option<E> fn decode_custom_error_to_enum(custom: u32) -> Option<E>
where where