Skip to content

Commit bed4ff4

Browse files
authored
RUST-880 Fix crash when deserializing/serializing Document that contains decimal128 (#285)
1 parent e81ffef commit bed4ff4

File tree

7 files changed

+49
-9
lines changed

7 files changed

+49
-9
lines changed

serde-tests/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ edition = "2018"
77
[dependencies]
88
bson = { path = ".." }
99
serde = { version = "1.0", features = ["derive"] }
10+
hex = "0.4"
1011

1112
[lib]
1213
name = "serde_tests"

serde-tests/test.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use serde::{self, de::Unexpected, Deserialize, Serialize};
55
use std::collections::{BTreeMap, HashSet};
66

7-
use bson::{Bson, Deserializer, Serializer};
7+
use bson::{Bson, Deserializer, Document, Serializer};
88

99
macro_rules! bson {
1010
([]) => {{ bson::Bson::Array(Vec::new()) }};
@@ -635,3 +635,23 @@ fn empty_arrays2() {
635635
});
636636
assert_eq!(v, t!(Deserialize::deserialize(d)));
637637
}
638+
639+
#[test]
640+
fn decimal128() {
641+
#[derive(Serialize, Deserialize)]
642+
struct Wrapper {
643+
d: Bson,
644+
}
645+
646+
// { "d": Decimal128("2") } from "Regular - 2" corpus test
647+
let bytes = hex::decode("180000001364000200000000000000000000000000403000").unwrap();
648+
let doc = Document::from_reader(&mut bytes.as_slice()).unwrap();
649+
650+
assert!(matches!(doc.get("d"), Some(Bson::Decimal128(_))));
651+
652+
let deserialized: Wrapper = bson::from_document(doc.clone()).unwrap();
653+
assert!(matches!(deserialized.d, Bson::Decimal128(_)));
654+
655+
let round = bson::to_document(&deserialized).unwrap();
656+
assert_eq!(round, doc);
657+
}

src/bson.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ impl Display for Bson {
122122
}) => write!(fmt, "/{}/{}", pattern, options),
123123
Bson::JavaScriptCode(ref code)
124124
| Bson::JavaScriptCodeWithScope(JavaScriptCodeWithScope { ref code, .. }) => {
125-
fmt.write_str(&code)
125+
fmt.write_str(code)
126126
}
127127
Bson::Int32(i) => write!(fmt, "{}", i),
128128
Bson::Int64(i) => write!(fmt, "{}", i),
@@ -574,6 +574,15 @@ impl Bson {
574574
"$numberDecimal": (v.to_string())
575575
}
576576
}
577+
#[cfg(not(feature = "decimal128"))]
578+
Bson::Decimal128(ref v) => {
579+
doc! {
580+
"$numberDecimalBytes": Bson::Binary(Binary {
581+
bytes: v.bytes.to_vec(),
582+
subtype: BinarySubtype::Generic,
583+
}).to_extended_document()
584+
}
585+
}
577586
Bson::Undefined => {
578587
doc! {
579588
"$undefined": true,
@@ -666,6 +675,16 @@ impl Bson {
666675
}
667676
}
668677

678+
#[cfg(not(feature = "decimal128"))]
679+
["$numberDecimalBytes"] => {
680+
if let Ok(d) = doc.get_binary_generic("$numberDecimalBytes") {
681+
let boxed_slice = d.clone().into_boxed_slice();
682+
if let Ok(ba) = Box::<[u8; 128 / 8]>::try_from(boxed_slice) {
683+
return Bson::Decimal128(Decimal128 { bytes: *ba });
684+
}
685+
}
686+
}
687+
669688
["$binary"] => {
670689
if let Some(binary) = Binary::from_extended_doc(&doc) {
671690
return Bson::Binary(binary);

src/de/serde.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ impl<'de> de::Deserializer<'de> for Deserializer {
325325
Bson::Binary(Binary {
326326
subtype: BinarySubtype::Generic,
327327
ref bytes,
328-
}) => visitor.visit_bytes(&bytes),
328+
}) => visitor.visit_bytes(bytes),
329329
binary @ Bson::Binary(..) => visitor.visit_map(MapDeserializer {
330330
iter: binary.to_extended_document().into_iter(),
331331
value: None,

src/extjson/de.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ impl TryFrom<serde_json::Value> for Bson {
200200
.or_else(|| x.as_f64().map(Bson::from))
201201
.ok_or_else(|| {
202202
Error::invalid_value(
203-
Unexpected::Other(&format!("{}", x).as_str()),
203+
Unexpected::Other(format!("{}", x).as_str()),
204204
&"a number that could fit in i32, i64, or f64",
205205
)
206206
}),

src/ser/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ pub(crate) fn serialize_bson<W: Write + ?Sized>(
110110

111111
match *val {
112112
Bson::Double(v) => write_f64(writer, v),
113-
Bson::String(ref v) => write_string(writer, &v),
114-
Bson::Array(ref v) => serialize_array(writer, &v),
113+
Bson::String(ref v) => write_string(writer, v),
114+
Bson::Array(ref v) => serialize_array(writer, v),
115115
Bson::Document(ref v) => v.to_writer(writer),
116116
Bson::Boolean(v) => writer
117117
.write_all(&[if v { 0x01 } else { 0x00 }])
@@ -123,7 +123,7 @@ pub(crate) fn serialize_bson<W: Write + ?Sized>(
123123
write_cstring(writer, pattern)?;
124124
write_cstring(writer, options)
125125
}
126-
Bson::JavaScriptCode(ref code) => write_string(writer, &code),
126+
Bson::JavaScriptCode(ref code) => write_string(writer, code),
127127
Bson::ObjectId(ref id) => writer.write_all(&id.bytes()).map_err(From::from),
128128
Bson::JavaScriptCodeWithScope(JavaScriptCodeWithScope {
129129
ref code,
@@ -160,7 +160,7 @@ pub(crate) fn serialize_bson<W: Write + ?Sized>(
160160
(v.timestamp() * 1000) + (v.nanosecond() / 1_000_000) as i64,
161161
),
162162
Bson::Null => Ok(()),
163-
Bson::Symbol(ref v) => write_string(writer, &v),
163+
Bson::Symbol(ref v) => write_string(writer, v),
164164
#[cfg(not(feature = "decimal128"))]
165165
Bson::Decimal128(ref v) => {
166166
writer.write_all(&v.bytes)?;

src/tests/serde.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ fn test_datetime_helpers() {
697697
}
698698
}
699699
}"#;
700-
let json: Value = serde_json::from_str(&date).unwrap();
700+
let json: Value = serde_json::from_str(date).unwrap();
701701
let b: B = serde_json::from_value(json).unwrap();
702702
let expected: chrono::DateTime<chrono::Utc> =
703703
chrono::DateTime::from_str("2020-06-09 10:58:07.095 UTC").unwrap();

0 commit comments

Comments
 (0)