diff --git a/ipld/bitfield/src/lib.rs b/ipld/bitfield/src/lib.rs index 6935c50a6..4e04a9f78 100644 --- a/ipld/bitfield/src/lib.rs +++ b/ipld/bitfield/src/lib.rs @@ -21,6 +21,12 @@ pub use rleplus::Error; use thiserror::Error; pub use unvalidated::{UnvalidatedBitField, Validate}; +/// MaxEncodedSize is the maximum encoded size of a bitfield. When expanded into +/// a slice of runs, a bitfield of this size should not exceed 2MiB of memory. +/// +/// This bitfield can fit at least 3072 sparse elements. +pub(crate) const MAX_ENCODED_SIZE: usize = 32 << 10; + #[derive(Clone, Error, Debug)] #[error("bitfields may not include u64::MAX")] pub struct OutOfRangeError; diff --git a/ipld/bitfield/src/rleplus/mod.rs b/ipld/bitfield/src/rleplus/mod.rs index f3bc0e35c..dfd8539d5 100644 --- a/ipld/bitfield/src/rleplus/mod.rs +++ b/ipld/bitfield/src/rleplus/mod.rs @@ -75,13 +75,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; pub use writer::BitWriter; use super::BitField; -use crate::RangeSize; - -// MaxEncodedSize is the maximum encoded size of a bitfield. When expanded into -// a slice of runs, a bitfield of this size should not exceed 2MiB of memory. -// -// This bitfield can fit at least 3072 sparse elements. -const MAX_ENCODED_SIZE: usize = 32 << 10; +use crate::{RangeSize, MAX_ENCODED_SIZE}; impl Serialize for BitField { fn serialize(&self, serializer: S) -> std::result::Result diff --git a/ipld/bitfield/src/unvalidated.rs b/ipld/bitfield/src/unvalidated.rs index d2412f6e7..f29c0c9e2 100644 --- a/ipld/bitfield/src/unvalidated.rs +++ b/ipld/bitfield/src/unvalidated.rs @@ -7,7 +7,7 @@ use fvm_ipld_encoding::serde_bytes; use serde::{Deserialize, Deserializer, Serialize}; use super::BitField; -use crate::Error; +use crate::{Error, MAX_ENCODED_SIZE}; /// A trait for types that can produce a `&BitField` (or fail to do so). /// Generalizes over `&BitField` and `&mut UnvalidatedBitField`. @@ -77,6 +77,12 @@ impl<'de> Deserialize<'de> for UnvalidatedBitField { D: Deserializer<'de>, { let bytes: Vec = serde_bytes::deserialize(deserializer)?; + if bytes.len() > MAX_ENCODED_SIZE { + return Err(serde::de::Error::custom(format!( + "encoded bitfield was too large {}", + bytes.len() + ))); + } Ok(Self::Unvalidated(bytes)) } } diff --git a/ipld/bitfield/tests/bitfield_tests.rs b/ipld/bitfield/tests/bitfield_tests.rs index 4dab4bd55..ee0df4553 100644 --- a/ipld/bitfield/tests/bitfield_tests.rs +++ b/ipld/bitfield/tests/bitfield_tests.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; -use fvm_ipld_bitfield::{bitfield, BitField}; +use fvm_ipld_bitfield::{bitfield, BitField, UnvalidatedBitField}; use rand::{Rng, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -232,3 +232,67 @@ fn exceeds_bitfield_range() { BitField::try_from_bits([0, 1, 4, 99, u64::MAX - 1]) .expect("expected setting u64::MAX-1 to succeed"); } + +#[test] +fn bitfield_custom() { + let mut bf = BitField::new(); + + // Set alternating bits for worst-case size performance + let mut i = 0; + while i < 1_000_000 { + bf.set(i); + i += 2; + } + println!("# Set bits: {}", bf.len()); + + // Standard serialization catches MAX_ENCODING_SIZE issues + println!("Attempting to serialize..."); + match fvm_ipld_encoding::to_vec(&bf) { + Ok(_) => panic!("This should have failed!"), + Err(_) => println!("Standard serialization failed, as expected"), + } + + // Bypass to_vec enc size check so we can test deserialization + println!("Manually serializing..."); + // CBOR prefix for the bytes + let mut cbor = vec![0x5A, 0x00, 0x01, 0xE8, 0x49]; + cbor.extend_from_slice(&bf.to_bytes()); + println!("Success!"); + + println!("# bytes of cbor: {}", cbor.len()); + println!("Header: {:#010b}", cbor[0]); + println!("-- maj type {}", (cbor[0] & 0xe0) >> 5); + + // Get size of payload size + let info = cbor[0] & 0x1f; + println!("-- adtl info {}", info); + + // Get payload size + let size = match info { + 0..=23 => info as usize, + 24 => cbor[1] as usize, + 25 => u16::from_be_bytes([cbor[1], cbor[2]]) as usize, + 26 => u32::from_be_bytes([cbor[1], cbor[2], cbor[3], cbor[4]]) as usize, + 27 => u64::from_be_bytes([ + cbor[1], cbor[2], cbor[3], cbor[4], cbor[5], cbor[6], cbor[7], cbor[8], + ]) as usize, + _ => { + println!("OUT OF RANGE"); + 0 + } + }; + + println!("{} byte payload", size); + + // Deserialize and validate malicious payload + println!("Attempting to deserialize and validate..."); + match fvm_ipld_encoding::from_slice::(&cbor) { + Ok(mut bitfield) => { + bitfield.validate_mut().unwrap(); + panic!("Error - deserialized/validated payload over 32768 bytes."); + } + Err(_) => { + println!("Success - payload over 32768 bytes cannot be deserialized"); + } + } +}