Skip to content

Commit 78585d7

Browse files
authored
Merge pull request #2074 from lucasbalieiro/add-fuzz-targets-datatypes
Add fuzz targets for sv2 datatypes
2 parents 2c70113 + 94e665d commit 78585d7

10 files changed

+454
-374
lines changed

fuzz/Cargo.toml

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,12 @@ path = "fuzz_targets/deserialize_sv2frame.rs"
3131
test = false
3232
doc = false
3333

34-
[[bin]]
35-
name = "deserialize_seq0255"
36-
path = "fuzz_targets/deserialize_seq0255.rs"
37-
test = false
38-
doc = false
39-
40-
[[bin]]
41-
name = "deserialize_seq064k"
42-
path = "fuzz_targets/deserialize_seq064k.rs"
43-
test = false
44-
doc = false
45-
4634
[[bin]]
4735
name = "deserialize_stdframe"
4836
path = "fuzz_targets/deserialize_stdframe.rs"
4937
test = false
5038
doc = false
5139

52-
[[bin]]
53-
name = "deserialize_datatypes"
54-
path = "fuzz_targets/deserialize_datatypes.rs"
55-
test = false
56-
doc = false
57-
5840
[[bin]]
5941
name = "end_to_end_serialization_for_common_messages"
6042
path = "fuzz_targets/end_to_end_serialization_for_common_messages.rs"
@@ -78,3 +60,10 @@ name = "end_to_end_serialization_for_template_distribution_messages"
7860
path = "fuzz_targets/end_to_end_serialization_for_template_distribution_messages.rs"
7961
test = false
8062
doc = false
63+
64+
[[bin]]
65+
name = "end_to_end_serialization_for_datatypes"
66+
path = "fuzz_targets/end_to_end_serialization_for_datatypes.rs"
67+
test = false
68+
doc = false
69+

fuzz/fuzz_targets/common.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,112 @@ macro_rules! test_roundtrip {
5757
};
5858
}};
5959
}
60+
61+
#[macro_export]
62+
macro_rules! test_datatype_roundtrip {
63+
// ---- special rule for bool ----
64+
// Bool has a non-canonical encoding in the spec: only the lowest bit is meaningful.
65+
// Multiple byte values can parse to the same logical bool, so we cannot require a
66+
// strict byte-for-byte roundtrip. Instead we check semantic stability and canonicalization.
67+
(bool, $data:expr) => {{
68+
let mut input = $data.clone();
69+
70+
// Only run the roundtrip checks if parsing succeeds. Invalid inputs are ignored,
71+
// because this macro validates stability of valid encodings, not rejection behavior.
72+
if let Ok(parsed) = bool::from_bytes(&mut input) {
73+
// Allocate exactly the number of bytes required by the parsed value.
74+
// This ensures we test the canonical serialized size.
75+
let mut encoded = vec![0u8; parsed.get_size()];
76+
77+
// A successful parse must always be serializable.
78+
parsed
79+
.to_bytes(&mut encoded)
80+
.expect("Bool encoding failed after a successfull parse");
81+
82+
// Bytes produced by serialization must always be parseable again.
83+
let reparsed = bool::from_bytes(&mut encoded)
84+
.expect("The bytes generated from a valid bool should be parseable");
85+
86+
// Logical value must be preserved by parse → serialize → parse.
87+
assert_eq!(parsed, reparsed, "Bool roundtrip is not stable");
88+
89+
// Because only the lowest bit is significant, we compare the semantic bit,
90+
// not the full original byte. This verifies canonical encoding.
91+
assert_eq!(input[0] & 1, encoded[0], "Bool serialization is not stable");
92+
}
93+
}};
94+
95+
// ---- special rule for f32 ----
96+
// Floats require bit-level comparison IEEE-754.
97+
(f32, $data:expr) => {{
98+
let mut input = $data.clone();
99+
100+
// Only validate successful parses; invalid encodings are outside this macro’s scope.
101+
if let Ok(parsed) = f32::from_bytes(&mut input) {
102+
// Allocate the exact canonical size of the float representation.
103+
let mut encoded = vec![0u8; parsed.get_size()];
104+
105+
// A successfully parsed float must serialize without failure.
106+
parsed
107+
.to_bytes(&mut encoded)
108+
.expect("Encoding failed after a successful parse");
109+
110+
// Serialized bytes must be parseable back into a float.
111+
let reparsed = f32::from_bytes(&mut encoded)
112+
.expect("The bytes generated from a valid datatype should be parseable");
113+
114+
// Compare raw bits to enforce strict roundtrip stability, including NaN payloads.
115+
assert_eq!(
116+
parsed.to_bits(),
117+
reparsed.to_bits(),
118+
"Float roundtrip is not bit-stable"
119+
);
120+
121+
// Ensure serialization is canonical: re-encoding must match the consumed input.
122+
assert_eq!(
123+
encoded,
124+
input[..encoded.get_size()],
125+
"Serialization is not stable"
126+
);
127+
}
128+
}};
129+
130+
// ---- generic rule ----
131+
($datatype:ty, $data:expr) => {{
132+
let mut input = $data.clone();
133+
134+
// Only test successful parses; this macro checks roundtrip invariants.
135+
if let Ok(parsed) = <$datatype>::from_bytes(&mut input) {
136+
// Allocate exactly the canonical serialized size.
137+
let mut encoded = vec![0u8; parsed.get_size()];
138+
139+
// A parsed value must always serialize successfully.
140+
parsed.clone().to_bytes(&mut encoded).expect(concat!(
141+
stringify!($datatype),
142+
": Encoding failed after a successful parse"
143+
));
144+
145+
// Serialized bytes must be parseable again into the same datatype.
146+
let reparsed = <$datatype>::from_bytes(&mut encoded).expect(concat!(
147+
stringify!($datatype),
148+
": The bytes generated from a valid datatype should be parseable"
149+
));
150+
151+
// Semantic equality after roundtrip is required.
152+
assert_eq!(
153+
parsed,
154+
reparsed,
155+
"{}: The roundtrip should produce the same message",
156+
stringify!($datatype)
157+
);
158+
159+
// reserialization must match the consumed input bytes.
160+
assert_eq!(
161+
encoded,
162+
input[..encoded.get_size()],
163+
"{}: Serialization is not stable",
164+
stringify!($datatype)
165+
);
166+
}
167+
}};
168+
}

fuzz/fuzz_targets/deserialize_datatypes.rs

Lines changed: 0 additions & 187 deletions
This file was deleted.

0 commit comments

Comments
 (0)