Fix Readable impl for Transaction (#795)

Fuzz test demonstrated that the current implemenation panics on reading from the network when serialized tx. Contains for example huge number of input or outputs. It allows a mailicious sender
to kill the server.
This commit is contained in:
Alexey Miroshkin
2018-03-16 20:02:22 +01:00
committed by Ignotus Peverell
parent cb71386097
commit 1bad7188b7
4 changed files with 30 additions and 1 deletions
+9
View File
@@ -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
+10
View File
@@ -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)?);
+6
View File
@@ -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)?;
+5 -1
View File
@@ -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<T>(reader: &mut Reader, count: u64) -> Result<Vec<
where
T: Readable + Hashed + Writeable,
{
let elem_size = mem::size_of::<T>();
if count.checked_mul(elem_size as u64).is_none() {
return Err(Error::TooLargeReadErr);
}
let result: Vec<T> = try!((0..count).map(|_| T::read(reader)).collect());
result.verify_sort_order()?;
Ok(result)