Skip to content

Commit

Permalink
fix: bignum parsed as Value
Browse files Browse the repository at this point in the history
Fixes: #31

Signed-off-by: Ahmed Charles <[email protected]>
  • Loading branch information
ahmedcharles committed Feb 18, 2024
1 parent 779edb4 commit ac202ec
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 37 deletions.
5 changes: 5 additions & 0 deletions ciborium-ll/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,11 @@ mod tests {
Header::Break,
],
),
("c340", &[Header::Tag(3), Header::Bytes(Some(0))]),
(
"c35fff",
&[Header::Tag(3), Header::Bytes(None), Header::Break],
),
];

for (bytes, headers) in data {
Expand Down
70 changes: 49 additions & 21 deletions ciborium/src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use alloc::{string::String, vec::Vec};

use ciborium_io::Read;
use ciborium_ll::*;
use serde::{de, de::Deserializer as _, forward_to_deserialize_any};
use serde::{
de::{self, value::BytesDeserializer, Deserializer as _},
forward_to_deserialize_any,
};

trait Expected<E: de::Error> {
fn expected(self, kind: &'static str) -> E;
Expand Down Expand Up @@ -72,7 +75,11 @@ where
}

#[inline]
fn integer(&mut self, mut header: Option<Header>) -> Result<(bool, u128), Error<R::Error>> {
fn integer<B: FnMut(u8)>(
&mut self,
mut header: Option<Header>,
mut append: Option<B>,
) -> Result<(bool, u128), Error<R::Error>> {
loop {
let header = match header.take() {
Some(h) => h,
Expand All @@ -99,7 +106,21 @@ where
while let Some(chunk) = segment.pull(&mut buffer)? {
for b in chunk {
match index {
16 => return Err(de::Error::custom("bigint too large")),
16 => {
if let Some(app) = append.as_mut() {
for v in value {
app(v);
}
app(*b);
index = 17;
continue;
}
return Err(de::Error::custom("bigint too large"));
}
17 => {
append.as_mut().unwrap()(*b);
continue;
}
0 if *b == 0 => continue, // Skip leading zeros
_ => value[index] = *b,
}
Expand All @@ -109,8 +130,12 @@ where
}
}

value[..index].reverse();
Ok((neg, u128::from_le_bytes(value)))
if index == 17 {
Ok((false, 0))
} else {
value[..index].reverse();
Ok((neg, u128::from_le_bytes(value)))
}
}

h => Err(h.expected("bytes")),
Expand Down Expand Up @@ -157,18 +182,21 @@ where
let header = self.decoder.pull()?;
self.decoder.push(header);

// If it is bytes, capture the length.
let len = match header {
Header::Bytes(x) => x,
_ => None,
};

match (tag, len) {
(tag::BIGPOS, Some(len)) | (tag::BIGNEG, Some(len)) if len <= 16 => {
let result = match self.integer(Some(Header::Tag(tag)))? {
(false, raw) => return visitor.visit_u128(raw),
(true, raw) => i128::try_from(raw).map(|x| x ^ !0),
};
match tag {
tag::BIGPOS | tag::BIGNEG => {
let mut bytes = Vec::new();
let result =
match self.integer(Some(Header::Tag(tag)), Some(|b| bytes.push(b)))? {
(false, _) if !bytes.is_empty() => {
let access = crate::tag::TagAccess::new(
BytesDeserializer::new(&bytes),
Some(tag),
);
return visitor.visit_enum(access);
}
(false, raw) => return visitor.visit_u128(raw),
(true, raw) => i128::try_from(raw).map(|x| x ^ !0),
};

match result {
Ok(x) => visitor.visit_i128(x),
Expand Down Expand Up @@ -238,7 +266,7 @@ where
}

fn deserialize_i64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
let result = match self.integer(None)? {
let result = match self.integer(None, None::<fn(_)>)? {
(false, raw) => i64::try_from(raw),
(true, raw) => i64::try_from(raw).map(|x| x ^ !0),
};
Expand All @@ -250,7 +278,7 @@ where
}

fn deserialize_i128<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
let result = match self.integer(None)? {
let result = match self.integer(None, None::<fn(_)>)? {
(false, raw) => i128::try_from(raw),
(true, raw) => i128::try_from(raw).map(|x| x ^ !0),
};
Expand All @@ -274,7 +302,7 @@ where
}

fn deserialize_u64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
let result = match self.integer(None)? {
let result = match self.integer(None, None::<fn(_)>)? {
(false, raw) => u64::try_from(raw),
(true, ..) => return Err(de::Error::custom("unexpected negative integer")),
};
Expand All @@ -286,7 +314,7 @@ where
}

fn deserialize_u128<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
match self.integer(None)? {
match self.integer(None, None::<fn(_)>)? {
(false, raw) => visitor.visit_u128(raw),
(true, ..) => Err(de::Error::custom("unexpected negative integer")),
}
Expand Down
29 changes: 13 additions & 16 deletions ciborium/tests/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,16 @@ macro_rules! map {
case(1i32, val!(1i32), "1b0000000000000001", true, same),
case(1i64, val!(1i64), "1b0000000000000001", true, same),
case(1i128, val!(1i128), "1b0000000000000001", true, same),
case(1u8, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1u16, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1u32, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1u64, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1u128, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1i8, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1i16, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1i32, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1i64, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1i128, bigint(), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1u8, val!(1), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1u16, val!(1), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1u32, val!(1), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1u64, val!(1), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1u128, val!(1), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1i8, val!(1), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1i16, val!(1), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1i32, val!(1), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1i64, val!(1), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(1i128, val!(1), "c2540000000000000000000000000000000000000001", true, same), // Not In RFC
case(10u8, val!(10u8), "0a", false, same),
case(10u16, val!(10u16), "0a", false, same),
case(10u32, val!(10u32), "0a", false, same),
Expand Down Expand Up @@ -274,6 +274,9 @@ macro_rules! map {
case(Enum::Newtype(45), cbor!({"Newtype" => 45}).unwrap(), "a1674e657774797065182d", false, same), // Not In RFC
case(Enum::Tuple(56, 67), cbor!({"Tuple" => [56, 67]}).unwrap(), "a1655475706c658218381843", false, same), // Not In RFC
case(Enum::Struct { first: 78, second: 89 }, cbor!({ "Struct" => { "first" => 78, "second" => 89 }}).unwrap(), "a166537472756374a2656669727374184e667365636f6e641859", false, same), // Not In RFC
case(hex!("0100000000000000000000000000000000000001"), Value::Tag(2, Value::Bytes(hex!("0100000000000000000000000000000000000001").to_vec()).into()), "c2540100000000000000000000000000000000000001", true, same), // Not In RFC
case(-1i8, val!(-1), "c340", true, same), // Not In RFC
case(-1i8, val!(-1), "c35fff", true, same), // Not In RFC
)]
fn codec<'de, T: Serialize + Clone, V: Debug + PartialEq + DeserializeOwned, F: Fn(T) -> V>(
input: T,
Expand Down Expand Up @@ -380,12 +383,6 @@ fn vmap_big() -> Value {
)
}

#[inline]
fn bigint() -> Value {
let bytes = hex::decode("0000000000000000000000000000000000000001").unwrap();
Value::Tag(2, Value::Bytes(bytes).into())
}

#[derive(Deserialize, Serialize, Copy, Clone, Debug, PartialEq, Eq)]
struct UnitStruct;

Expand Down

0 comments on commit ac202ec

Please sign in to comment.