Skip to content

Commit

Permalink
test(packet): introduce roundtrip-only feature
Browse files Browse the repository at this point in the history
this follows from finding a case where the parsed timestamp doesn't
match the output timestamp, even with the fix made in interledger#707. the strict
feature is included in the "rountrip-only".

Signed-off-by: Joonas Koivunen <[email protected]>
  • Loading branch information
koivunej authored and Mirko-von-Leipzig committed Aug 3, 2021
1 parent f45f338 commit 3445304
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 6 deletions.
4 changes: 3 additions & 1 deletion crates/interledger-packet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ edition = "2018"
repository = "https://github.com/interledger-rs/interledger-rs"

[features]
# used when fuzzing; accepts only roundtripping input
# strict adherence to the rfcs, but not taking any "roundtrip-only" shortcuts
strict = []
# used when fuzzing; accepts only roundtripping input
roundtrip-only = ["strict"]

[dependencies]
byteorder = { version = "1.3.2" }
Expand Down
2 changes: 1 addition & 1 deletion crates/interledger-packet/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ bytes = "0.5"

[dependencies.interledger-packet]
path = ".."
features = ["strict"]
features = ["roundtrip-only"]

# Prevent this from interfering with workspaces
[workspace]
Expand Down
56 changes: 52 additions & 4 deletions crates/interledger-packet/src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,22 +135,41 @@ impl TryFrom<BytesMut> for Prepare {

// Fixed Length DateTime format - RFC 0027
// https://github.com/interledger/rfcs/blob/2dfdcf47ac52489a4ad473a5d869cd9f0217db67/0027-interledger-protocol-4/0027-interledger-protocol-4.md#ilp-prepare
let mut expires_at = [0x00; 17];
content.read_exact(&mut expires_at)?;
let mut read_expires_at = [0x00; 17];
content.read_exact(&mut read_expires_at)?;

if !expires_at
if !read_expires_at
.iter()
.map(|e| (b'0'..=b'9').contains(e))
.fold(true, |a, b| a & b)
{
return Err(ParseError::InvalidPacket("DateTime must be numeric".into()));
}

let expires_at = str::from_utf8(&expires_at[..])?;
let expires_at = str::from_utf8(&read_expires_at[..])?;
let expires_at: DateTime<Utc> =
Utc.datetime_from_str(&expires_at, INTERLEDGER_TIMESTAMP_FORMAT)?;
let expires_at = SystemTime::from(expires_at);

if cfg!(feature = "roundtrip-only") {
// chrono will leniently parse some timestamps into forms which don't roundtrip.
// this works around the class of fuzzer findings demonstrated by
// fuzzed_1_chrono_60s_rollover.
let mut roundtripped = [0u8; 17];
write!(
&mut roundtripped[..],
"{}",
DateTime::<Utc>::from(expires_at).format(INTERLEDGER_TIMESTAMP_FORMAT),
)
.unwrap();

if roundtripped != read_expires_at {
return Err(ParseError::InvalidPacket(
"Non-roundtripping datetime".into(),
));
}
}

// Skip execution condition.
content.skip(CONDITION_LEN)?;

Expand Down Expand Up @@ -718,6 +737,35 @@ mod fuzzed {
res.unwrap();
}
}

#[test]
#[cfg(feature = "roundtrip-only")]
fn fuzzed_1_chrono_60s_rollover() {
// this has been reduced from the original
#[rustfmt::skip]
let orig = [
// prepare
12,
// variable length len
65,
// amount
0, 0, 0, 0, 0, 0, 0, 1,
// timestamp, "2000-05-31T16:01:60.251" which is parsed and later formatted as
// "2000-05-31T16:02:00.251" (dashes, T, colons and dot added for readability)
50, 48, 48, 48, 48, 53, 51, 49, 49, 54, 48, 49, 54, 48, 50, 53, 49,
// execution condition
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
// destination varlen
6,
// destination "test.a"
116, 101, 115, 116, 46, 97,
// data varlen
0,
];

crate::lenient_packet_roundtrips(&orig[..]).unwrap_err();
}
}

#[cfg(test)]
Expand Down

0 comments on commit 3445304

Please sign in to comment.