Fail secp256k1 if the instruction data looks incorrect (#19300)

This commit is contained in:
Jack May 2021-08-19 13:13:54 -07:00 committed by GitHub
parent f59a55be17
commit 3ec33e7d02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 74 additions and 12 deletions

View File

@ -131,10 +131,14 @@ impl BanksServer {
fn verify_transaction( fn verify_transaction(
transaction: &Transaction, transaction: &Transaction,
libsecp256k1_0_5_upgrade_enabled: bool, libsecp256k1_0_5_upgrade_enabled: bool,
libsecp256k1_fail_on_bad_count: bool,
) -> transaction::Result<()> { ) -> transaction::Result<()> {
if let Err(err) = transaction.verify() { if let Err(err) = transaction.verify() {
Err(err) Err(err)
} else if let Err(err) = transaction.verify_precompiles(libsecp256k1_0_5_upgrade_enabled) { } else if let Err(err) = transaction.verify_precompiles(
libsecp256k1_0_5_upgrade_enabled,
libsecp256k1_fail_on_bad_count,
) {
Err(err) Err(err)
} else { } else {
Ok(()) Ok(())
@ -228,6 +232,7 @@ impl Banks for BanksServer {
if let Err(err) = verify_transaction( if let Err(err) = verify_transaction(
&transaction, &transaction,
self.bank(commitment).libsecp256k1_0_5_upgrade_enabled(), self.bank(commitment).libsecp256k1_0_5_upgrade_enabled(),
self.bank(commitment).libsecp256k1_fail_on_bad_count(),
) { ) {
return Some(Err(err)); return Some(Err(err));
} }

View File

@ -1046,6 +1046,7 @@ impl BankingStage {
msgs: &Packets, msgs: &Packets,
transaction_indexes: &[usize], transaction_indexes: &[usize],
libsecp256k1_0_5_upgrade_enabled: bool, libsecp256k1_0_5_upgrade_enabled: bool,
libsecp256k1_fail_on_bad_count: bool,
cost_tracker: &Arc<RwLock<CostTracker>>, cost_tracker: &Arc<RwLock<CostTracker>>,
banking_stage_stats: &BankingStageStats, banking_stage_stats: &BankingStageStats,
) -> (Vec<SanitizedTransaction>, Vec<usize>, Vec<usize>) { ) -> (Vec<SanitizedTransaction>, Vec<usize>, Vec<usize>) {
@ -1062,7 +1063,10 @@ impl BankingStage {
Err(TransactionError::UnsupportedVersion) Err(TransactionError::UnsupportedVersion)
}) })
.ok()?; .ok()?;
tx.verify_precompiles(libsecp256k1_0_5_upgrade_enabled) tx.verify_precompiles(
libsecp256k1_0_5_upgrade_enabled,
libsecp256k1_fail_on_bad_count,
)
.ok()?; .ok()?;
Some((tx, *tx_index)) Some((tx, *tx_index))
}) })
@ -1158,6 +1162,7 @@ impl BankingStage {
msgs, msgs,
&packet_indexes, &packet_indexes,
bank.libsecp256k1_0_5_upgrade_enabled(), bank.libsecp256k1_0_5_upgrade_enabled(),
bank.libsecp256k1_fail_on_bad_count(),
cost_tracker, cost_tracker,
banking_stage_stats, banking_stage_stats,
); );
@ -1260,6 +1265,7 @@ impl BankingStage {
msgs, msgs,
transaction_indexes, transaction_indexes,
bank.libsecp256k1_0_5_upgrade_enabled(), bank.libsecp256k1_0_5_upgrade_enabled(),
bank.libsecp256k1_fail_on_bad_count(),
cost_tracker, cost_tracker,
banking_stage_stats, banking_stage_stats,
); );

View File

@ -44,7 +44,7 @@ pub mod test {
Hash::default(), Hash::default(),
); );
assert!(tx.verify_precompiles(false).is_ok()); assert!(tx.verify_precompiles(false, true).is_ok());
let index = thread_rng().gen_range(0, secp_instruction.data.len()); let index = thread_rng().gen_range(0, secp_instruction.data.len());
secp_instruction.data[index] = secp_instruction.data[index].wrapping_add(12); secp_instruction.data[index] = secp_instruction.data[index].wrapping_add(12);
@ -54,6 +54,6 @@ pub mod test {
&[&mint_keypair], &[&mint_keypair],
Hash::default(), Hash::default(),
); );
assert!(tx.verify_precompiles(false).is_err()); assert!(tx.verify_precompiles(false, true).is_err());
} }
} }

View File

@ -1965,12 +1965,16 @@ impl JsonRpcRequestProcessor {
fn verify_transaction( fn verify_transaction(
transaction: &SanitizedTransaction, transaction: &SanitizedTransaction,
libsecp256k1_0_5_upgrade_enabled: bool, libsecp256k1_0_5_upgrade_enabled: bool,
libsecp256k1_fail_on_bad_count: bool,
) -> Result<()> { ) -> Result<()> {
if transaction.verify().is_err() { if transaction.verify().is_err() {
return Err(RpcCustomError::TransactionSignatureVerificationFailure.into()); return Err(RpcCustomError::TransactionSignatureVerificationFailure.into());
} }
if let Err(e) = transaction.verify_precompiles(libsecp256k1_0_5_upgrade_enabled) { if let Err(e) = transaction.verify_precompiles(
libsecp256k1_0_5_upgrade_enabled,
libsecp256k1_fail_on_bad_count,
) {
return Err(RpcCustomError::TransactionPrecompileVerificationFailure(e).into()); return Err(RpcCustomError::TransactionPrecompileVerificationFailure(e).into());
} }
@ -3324,6 +3328,7 @@ pub mod rpc_full {
if let Err(e) = verify_transaction( if let Err(e) = verify_transaction(
&transaction, &transaction,
preflight_bank.libsecp256k1_0_5_upgrade_enabled(), preflight_bank.libsecp256k1_0_5_upgrade_enabled(),
preflight_bank.libsecp256k1_fail_on_bad_count(),
) { ) {
return Err(e); return Err(e);
} }
@ -3409,7 +3414,11 @@ pub mod rpc_full {
let transaction = sanitize_transaction(unsanitized_tx)?; let transaction = sanitize_transaction(unsanitized_tx)?;
if config.sig_verify { if config.sig_verify {
verify_transaction(&transaction, bank.libsecp256k1_0_5_upgrade_enabled())?; verify_transaction(
&transaction,
bank.libsecp256k1_0_5_upgrade_enabled(),
bank.libsecp256k1_fail_on_bad_count(),
)?;
} }
let TransactionSimulationResult { let TransactionSimulationResult {

View File

@ -5011,7 +5011,7 @@ impl Bank {
} }
if !skip_verification { if !skip_verification {
sanitized_tx.verify_precompiles(self.libsecp256k1_0_5_upgrade_enabled())?; sanitized_tx.verify_precompiles(self.libsecp256k1_0_5_upgrade_enabled(), true)?;
} }
Ok(sanitized_tx) Ok(sanitized_tx)
@ -5467,6 +5467,11 @@ impl Bank {
.is_active(&feature_set::libsecp256k1_0_5_upgrade_enabled::id()) .is_active(&feature_set::libsecp256k1_0_5_upgrade_enabled::id())
} }
pub fn libsecp256k1_fail_on_bad_count(&self) -> bool {
self.feature_set
.is_active(&feature_set::libsecp256k1_fail_on_bad_count::id())
}
pub fn merge_nonce_error_into_system_error(&self) -> bool { pub fn merge_nonce_error_into_system_error(&self) -> bool {
self.feature_set self.feature_set
.is_active(&feature_set::merge_nonce_error_into_system_error::id()) .is_active(&feature_set::merge_nonce_error_into_system_error::id())

View File

@ -199,6 +199,10 @@ pub mod versioned_tx_message_enabled {
solana_sdk::declare_id!("3KZZ6Ks1885aGBQ45fwRcPXVBCtzUvxhUTkwKMR41Tca"); solana_sdk::declare_id!("3KZZ6Ks1885aGBQ45fwRcPXVBCtzUvxhUTkwKMR41Tca");
} }
pub mod libsecp256k1_fail_on_bad_count {
solana_sdk::declare_id!("8aXvSuopd1PUj7UhehfXJRg6619RHp8ZvwTyyJHdUYsj");
}
lazy_static! { lazy_static! {
/// Map of feature identifiers to user-visible description /// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [ pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -243,6 +247,7 @@ lazy_static! {
(gate_large_block::id(), "validator checks block cost against max limit in realtime, reject if exceeds."), (gate_large_block::id(), "validator checks block cost against max limit in realtime, reject if exceeds."),
(mem_overlap_fix::id(), "memory overlap fix"), (mem_overlap_fix::id(), "memory overlap fix"),
(versioned_tx_message_enabled::id(), "enable versioned transaction message processing"), (versioned_tx_message_enabled::id(), "enable versioned transaction message processing"),
(libsecp256k1_fail_on_bad_count::id(), "Fail libsec256k1_verify if count appears wrong")
/*************** ADD NEW FEATURES HERE ***************/ /*************** ADD NEW FEATURES HERE ***************/
] ]
.iter() .iter()

View File

@ -103,11 +103,18 @@ pub fn verify_eth_addresses(
data: &[u8], data: &[u8],
instruction_datas: &[&[u8]], instruction_datas: &[&[u8]],
libsecp256k1_0_5_upgrade_enabled: bool, libsecp256k1_0_5_upgrade_enabled: bool,
libsecp256k1_fail_on_bad_count: bool,
) -> Result<(), Secp256k1Error> { ) -> Result<(), Secp256k1Error> {
if data.is_empty() { if data.is_empty() {
return Err(Secp256k1Error::InvalidInstructionDataSize); return Err(Secp256k1Error::InvalidInstructionDataSize);
} }
let count = data[0] as usize; let count = data[0] as usize;
if libsecp256k1_fail_on_bad_count && count == 0 && data.len() > 1 {
// count is zero but the instruction data indicates that is probably not
// correct, fail the instruction to catch probable invalid secp256k1
// instruction construction.
return Err(Secp256k1Error::InvalidInstructionDataSize);
}
let expected_data_size = count let expected_data_size = count
.saturating_mul(SIGNATURE_OFFSETS_SERIALIZED_SIZE) .saturating_mul(SIGNATURE_OFFSETS_SERIALIZED_SIZE)
.saturating_add(1); .saturating_add(1);
@ -215,7 +222,7 @@ pub mod test {
let writer = std::io::Cursor::new(&mut instruction_data[1..]); let writer = std::io::Cursor::new(&mut instruction_data[1..]);
bincode::serialize_into(writer, &offsets).unwrap(); bincode::serialize_into(writer, &offsets).unwrap();
verify_eth_addresses(&instruction_data, &[&[0u8; 100]], false) verify_eth_addresses(&instruction_data, &[&[0u8; 100]], false, true)
} }
#[test] #[test]
@ -230,7 +237,7 @@ pub mod test {
instruction_data.truncate(instruction_data.len() - 1); instruction_data.truncate(instruction_data.len() - 1);
assert_eq!( assert_eq!(
verify_eth_addresses(&instruction_data, &[&[0u8; 100]], false), verify_eth_addresses(&instruction_data, &[&[0u8; 100]], false, true),
Err(Secp256k1Error::InvalidInstructionDataSize) Err(Secp256k1Error::InvalidInstructionDataSize)
); );
@ -346,4 +353,19 @@ pub mod test {
Err(Secp256k1Error::InvalidSignature) Err(Secp256k1Error::InvalidSignature)
); );
} }
#[test]
fn test_count_is_zero_but_sig_data_exists() {
solana_logger::setup();
let mut instruction_data = vec![0u8; DATA_START];
let offsets = SecpSignatureOffsets::default();
instruction_data[0] = 0;
let writer = std::io::Cursor::new(&mut instruction_data[1..]);
bincode::serialize_into(writer, &offsets).unwrap();
assert_eq!(
verify_eth_addresses(&instruction_data, &[&[0u8; 100]], false, true),
Err(Secp256k1Error::InvalidInstructionDataSize)
);
}
} }

View File

@ -426,7 +426,11 @@ impl Transaction {
.collect() .collect()
} }
pub fn verify_precompiles(&self, libsecp256k1_0_5_upgrade_enabled: bool) -> Result<()> { pub fn verify_precompiles(
&self,
libsecp256k1_0_5_upgrade_enabled: bool,
libsecp256k1_fail_on_bad_count: bool,
) -> Result<()> {
for instruction in &self.message().instructions { for instruction in &self.message().instructions {
// The Transaction may not be sanitized at this point // The Transaction may not be sanitized at this point
if instruction.program_id_index as usize >= self.message().account_keys.len() { if instruction.program_id_index as usize >= self.message().account_keys.len() {
@ -445,6 +449,7 @@ impl Transaction {
data, data,
&instruction_datas, &instruction_datas,
libsecp256k1_0_5_upgrade_enabled, libsecp256k1_0_5_upgrade_enabled,
libsecp256k1_fail_on_bad_count,
); );
e.map_err(|_| TransactionError::InvalidAccountIndex)?; e.map_err(|_| TransactionError::InvalidAccountIndex)?;
} }

View File

@ -203,7 +203,11 @@ impl SanitizedTransaction {
} }
/// Verify the encoded secp256k1 signatures in this transaction /// Verify the encoded secp256k1 signatures in this transaction
pub fn verify_precompiles(&self, libsecp256k1_0_5_upgrade_enabled: bool) -> Result<()> { pub fn verify_precompiles(
&self,
libsecp256k1_0_5_upgrade_enabled: bool,
libsecp256k1_fail_on_bad_count: bool,
) -> Result<()> {
for (program_id, instruction) in self.message.program_instructions_iter() { for (program_id, instruction) in self.message.program_instructions_iter() {
if secp256k1_program::check_id(program_id) { if secp256k1_program::check_id(program_id) {
let instruction_datas: Vec<_> = self let instruction_datas: Vec<_> = self
@ -217,6 +221,7 @@ impl SanitizedTransaction {
data, data,
&instruction_datas, &instruction_datas,
libsecp256k1_0_5_upgrade_enabled, libsecp256k1_0_5_upgrade_enabled,
libsecp256k1_fail_on_bad_count,
); );
e.map_err(|_| TransactionError::InvalidAccountIndex)?; e.map_err(|_| TransactionError::InvalidAccountIndex)?;
} }