Skip to content

Commit 54b4283

Browse files
fix(shared): limit BigInt encoding and decoding sizes
1 parent 74090ff commit 54b4283

File tree

3 files changed

+112
-2
lines changed

3 files changed

+112
-2
lines changed

shared/src/bigint/bigint_ser.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ use std::borrow::Cow;
66
use num_bigint::{BigInt, Sign};
77
use serde::{Deserialize, Serialize};
88

9+
use super::MAX_BIGINT_SIZE;
10+
911
/// Wrapper for serializing big ints to match filecoin spec. Serializes as bytes.
1012
#[derive(Serialize)]
1113
#[serde(transparent)]
1214
pub struct BigIntSer<'a>(#[serde(with = "self")] pub &'a BigInt);
1315

1416
/// Wrapper for deserializing as BigInt from bytes.
15-
#[derive(Deserialize, Serialize, Clone, Default, PartialEq)]
17+
#[derive(Deserialize, Serialize, Clone, Default, PartialEq, Debug)]
1618
#[serde(transparent)]
1719
pub struct BigIntDe(#[serde(with = "self")] pub BigInt);
1820

@@ -30,6 +32,10 @@ where
3032
Sign::NoSign => bz = Vec::new(),
3133
}
3234

35+
if bz.len() > MAX_BIGINT_SIZE {
36+
return Err(<S::Error as serde::ser::Error>::custom("BigInt too large"));
37+
}
38+
3339
// Serialize as bytes
3440
serde_bytes::Serialize::serialize(&bz, serializer)
3541
}
@@ -53,5 +59,59 @@ where
5359
));
5460
}
5561
};
62+
63+
if bz.len() > MAX_BIGINT_SIZE {
64+
return Err(<D::Error as serde::de::Error>::custom("BigInt too large"));
65+
}
66+
5667
Ok(BigInt::from_bytes_be(sign, &bz[1..]))
5768
}
69+
70+
#[cfg(test)]
71+
mod tests {
72+
use fvm_ipld_encoding::{from_slice, to_vec};
73+
74+
use super::*;
75+
76+
#[test]
77+
fn test_bigiint_max() {
78+
let max_limbs = MAX_BIGINT_SIZE / 4; // 32bit limbs to bytes
79+
let good = BigInt::new(Sign::Plus, vec![u32::MAX; max_limbs - 1]);
80+
let good_neg = BigInt::new(Sign::Minus, vec![u32::MAX; max_limbs - 1]);
81+
82+
let good_bytes = to_vec(&BigIntSer(&good)).expect("should be good");
83+
let good_back: BigIntDe = from_slice(&good_bytes).unwrap();
84+
assert_eq!(good_back.0, good);
85+
86+
let good_neg_bytes = to_vec(&BigIntSer(&good_neg)).expect("should be good");
87+
let good_neg_back: BigIntDe = from_slice(&good_neg_bytes).unwrap();
88+
assert_eq!(good_neg_back.0, good_neg);
89+
90+
// max limbs will fail as the sign is prepended
91+
let bad1 = BigInt::new(Sign::Plus, vec![u32::MAX; max_limbs]);
92+
let bad1_neg = BigInt::new(Sign::Minus, vec![u32::MAX; max_limbs]);
93+
let bad2 = BigInt::new(Sign::Plus, vec![u32::MAX; max_limbs + 1]);
94+
let bad2_neg = BigInt::new(Sign::Minus, vec![u32::MAX; max_limbs + 1]);
95+
96+
assert!(to_vec(&BigIntSer(&bad1)).is_err());
97+
assert!(to_vec(&BigIntSer(&bad1_neg)).is_err());
98+
assert!(to_vec(&BigIntSer(&bad2)).is_err());
99+
assert!(to_vec(&BigIntSer(&bad2_neg)).is_err());
100+
101+
let bad_bytes = {
102+
let (sign, mut source) = bad1.to_bytes_be();
103+
match sign {
104+
Sign::Minus => source.insert(0, 0),
105+
_ => source.insert(0, 1),
106+
}
107+
to_vec(&serde_bytes::Bytes::new(&source)).unwrap()
108+
};
109+
110+
let res: Result<BigIntDe, _> = from_slice(&bad_bytes);
111+
assert!(res.is_err());
112+
assert_eq!(
113+
res.unwrap_err().to_string(),
114+
"Serialization error for Cbor protocol: BigInt too large"
115+
);
116+
}
117+
}

shared/src/bigint/biguint_ser.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ use std::borrow::Cow;
66
use num_bigint::BigUint;
77
use serde::{Deserialize, Serialize};
88

9+
use super::MAX_BIGINT_SIZE;
10+
911
/// Wrapper for serializing big ints to match filecoin spec. Serializes as bytes.
1012
#[derive(Serialize)]
1113
#[serde(transparent)]
1214
pub struct BigUintSer<'a>(#[serde(with = "self")] pub &'a BigUint);
1315

1416
/// Wrapper for deserializing as BigUint from bytes.
15-
#[derive(Deserialize, Serialize, Clone)]
17+
#[derive(Deserialize, Serialize, Clone, Debug)]
1618
#[serde(transparent)]
1719
pub struct BigUintDe(#[serde(with = "self")] pub BigUint);
1820

@@ -29,6 +31,10 @@ where
2931
bz.insert(0, 0);
3032
}
3133

34+
if dbg!(bz.len()) > MAX_BIGINT_SIZE {
35+
return Err(<S::Error as serde::ser::Error>::custom("BigInt too large"));
36+
}
37+
3238
// Serialize as bytes
3339
serde_bytes::Serialize::serialize(&bz, serializer)
3440
}
@@ -48,5 +54,46 @@ where
4854
));
4955
}
5056

57+
if bz.len() > MAX_BIGINT_SIZE {
58+
return Err(<D::Error as serde::de::Error>::custom("BigInt too large"));
59+
}
60+
5161
Ok(BigUint::from_bytes_be(&bz[1..]))
5262
}
63+
64+
#[cfg(test)]
65+
mod tests {
66+
use fvm_ipld_encoding::{from_slice, to_vec};
67+
68+
use super::*;
69+
70+
#[test]
71+
fn test_biguint_max() {
72+
let max_limbs = MAX_BIGINT_SIZE / 4; // 32bit limbs to bytes
73+
let good = BigUint::new(vec![u32::MAX; max_limbs - 1]);
74+
75+
let good_bytes = to_vec(&BigUintSer(&good)).expect("should be good");
76+
let good_back: BigUintDe = from_slice(&good_bytes).unwrap();
77+
assert_eq!(good_back.0, good);
78+
79+
// max limbs will fail as the sign is prepended
80+
let bad1 = BigUint::new(vec![u32::MAX; max_limbs]);
81+
let bad2 = BigUint::new(vec![u32::MAX; max_limbs + 1]);
82+
83+
assert!(to_vec(&BigUintSer(&bad1)).is_err());
84+
assert!(to_vec(&BigUintSer(&bad2)).is_err());
85+
86+
let bad_bytes = {
87+
let mut source = bad1.to_bytes_be();
88+
source.insert(0, 0);
89+
to_vec(&serde_bytes::Bytes::new(&source)).unwrap()
90+
};
91+
92+
let res: Result<BigUintDe, _> = from_slice(&bad_bytes);
93+
assert!(res.is_err());
94+
assert_eq!(
95+
res.unwrap_err().to_string(),
96+
"Serialization error for Cbor protocol: BigInt too large"
97+
);
98+
}
99+
}

shared/src/bigint/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ pub mod biguint_ser;
77
pub use num_bigint::*;
88
pub use num_integer::{self, Integer};
99
pub use num_traits::Zero;
10+
11+
/// The maximum number of bytes we accept to serialize/deserialize for a single BigInt.
12+
pub const MAX_BIGINT_SIZE: usize = 128;

0 commit comments

Comments
 (0)