Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make sure bitfield is within limits #535

Merged
merged 1 commit into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ipld/bitfield/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 1 addition & 7 deletions ipld/bitfield/src/rleplus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
Expand Down
8 changes: 7 additions & 1 deletion ipld/bitfield/src/unvalidated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down Expand Up @@ -77,6 +77,12 @@ impl<'de> Deserialize<'de> for UnvalidatedBitField {
D: Deserializer<'de>,
{
let bytes: Vec<u8> = 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))
}
}
66 changes: 65 additions & 1 deletion ipld/bitfield/tests/bitfield_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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::<UnvalidatedBitField>(&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");
}
}
}