diff --git a/core/src/consensus.rs b/core/src/consensus.rs index 9d8d5b66..0b29b365 100644 --- a/core/src/consensus.rs +++ b/core/src/consensus.rs @@ -88,6 +88,15 @@ pub const MAX_BLOCK_WEIGHT: usize = 80_000; /// Hundreds of inputs + 1 output might be slow to validate (issue#258) pub const MAX_BLOCK_INPUTS: usize = 300_000; // soft fork down when too_high +/// Maixmum inputs for a transaction +pub const MAX_TX_INPUTS: u64 = 500; + +/// Maixmum outputs for a transaction +pub const MAX_TX_OUTPUTS: u64 = 500; // wallet uses 500 as max + +/// Maixmum kernels for a transaction +pub const MAX_TX_KERNELS: u64 = 2048; + /// Whether a block exceeds the maximum acceptable weight pub fn exceeds_weight(input_len: usize, output_len: usize, kernel_len: usize) -> bool { input_len * BLOCK_INPUT_WEIGHT + output_len * BLOCK_OUTPUT_WEIGHT diff --git a/core/src/core/pmmr.rs b/core/src/core/pmmr.rs index c7fc885d..0b5cfc62 100644 --- a/core/src/core/pmmr.rs +++ b/core/src/core/pmmr.rs @@ -90,6 +90,12 @@ where fn dump_stats(&self); } +/// Maixmum peeks for a Merkle proof +pub const MAX_PEAKS: u64 = 300_000; + +/// Maixmum path for a Merkle proof +pub const MAX_PATH: u64 = 300_000; + /// A Merkle proof. /// Proves inclusion of an output (node) in the output MMR. /// We can use this to prove an output was unspent at the time of a given block @@ -134,6 +140,10 @@ impl Readable for MerkleProof { let (peaks_len, path_len) = ser_multiread!(reader, read_u64, read_u64); + if peaks_len > MAX_PEAKS || path_len > MAX_PATH { + return Err(ser::Error::CorruptedData); + } + let mut peaks = Vec::with_capacity(peaks_len as usize); for _ in 0..peaks_len { peaks.push(Hash::read(reader)?); diff --git a/core/src/core/transaction.rs b/core/src/core/transaction.rs index f24dcc7e..f02ad794 100644 --- a/core/src/core/transaction.rs +++ b/core/src/core/transaction.rs @@ -302,6 +302,12 @@ impl Readable for Transaction { let (input_len, output_len, kernel_len) = ser_multiread!(reader, read_u64, read_u64, read_u64); + if input_len > consensus::MAX_TX_INPUTS + || output_len > consensus::MAX_TX_OUTPUTS + || kernel_len > consensus::MAX_TX_KERNELS { + return Err(ser::Error::CorruptedData) + } + let inputs = read_and_verify_sorted(reader, input_len)?; let outputs = read_and_verify_sorted(reader, output_len)?; let kernels = read_and_verify_sorted(reader, kernel_len)?; diff --git a/core/src/ser.rs b/core/src/ser.rs index 71565259..c4e03153 100644 --- a/core/src/ser.rs +++ b/core/src/ser.rs @@ -19,7 +19,7 @@ //! To use it simply implement `Writeable` or `Readable` and then use the //! `serialize` or `deserialize` functions on them as appropriate. -use std::{cmp, error, fmt}; +use std::{cmp, error, fmt, mem}; use std::io::{self, Read, Write}; use byteorder::{BigEndian, ByteOrder, ReadBytesExt}; use keychain::{BlindingFactor, Identifier, IDENTIFIER_SIZE}; @@ -219,6 +219,10 @@ pub fn read_and_verify_sorted(reader: &mut Reader, count: u64) -> Result(); + if count.checked_mul(elem_size as u64).is_none() { + return Err(Error::TooLargeReadErr); + } let result: Vec = try!((0..count).map(|_| T::read(reader)).collect()); result.verify_sort_order()?; Ok(result)