Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ jobs:
- fuzz_target: record_encode_decode
corpus: ""
features: ''
- fuzz_target: ptp_message_parsing
corpus: ""
features: ''
steps:
- name: Checkout sources
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
Expand Down
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 12 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
members = [
"ntp-proto",
"ntpd"
]
, "statime-wire"]
exclude = [ ]

# Properly take compiler version into account when resolving crates.
Expand Down Expand Up @@ -31,19 +31,19 @@ panic = "abort"
debug = 2

[workspace.dependencies]
arbitrary = { version = "1.0" }
clock-steering = "0.2.1"
hickory-resolver = { version = "0.25.0", features = ["dnssec-aws-lc-rs"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.0", default-features = false, features = ["std", "fmt", "ansi"] }
libc = "0.2.154"
pps-time = "0.2.3"
rand = "0.8.0"
serde = { version = "1.0.145", features = ["derive"] }
serde_json = "1.0"
rand = "0.8.0"
arbitrary = { version = "1.0" }
libc = "0.2.154"
timestamped-socket = "0.2.2"
tokio = "1.32"
toml = { version = ">=0.6.0,<0.9.0", default-features = false, features = ["parse"] }
timestamped-socket = "0.2.2"
clock-steering = "0.2.1"
pps-time = "0.2.3"
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.0", default-features = false, features = ["std", "fmt", "ansi"] }

# TLS
rustls23 = { package = "rustls", version = "0.23.16", features = ["logging", "std"] }
Expand All @@ -57,6 +57,9 @@ aes-siv = "0.7.0"
md-5 = "0.10.0"
zeroize = "1.7"

# development
serde_test = { version = "1.0.176" }

# our own crates used as dependencies, same version as the workspace version
# NOTE: keep this part at the bottom of the file, do not change this line
ntp-proto = { version = "1.7.2", path = "./ntp-proto", default-features = false, features = ["__internal-api"] }
8 changes: 8 additions & 0 deletions fuzz/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ features = ["arbitrary-derive"]
path = "../ntp-proto"
features = ["__internal-fuzz"]

[dependencies.statime-wire]
path = "../statime-wire"

# Prevent this from interfering with workspaces
[workspace]
members = ["."]
Expand Down Expand Up @@ -66,6 +69,12 @@ path = "fuzz_targets/ipfilter.rs"
test = false
doc = false

[[bin]]
name = "ptp_message_parsing"
path = "fuzz_targets/ptp_message_parsing.rs"
test = false
doc = false

[[bin]]
name = "record_encode_decode"
path = "fuzz_targets/record_encode_decode.rs"
Expand Down
30 changes: 30 additions & 0 deletions fuzz/fuzz_targets/ptp_message_parsing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#![no_main]

use libfuzzer_sys::fuzz_target;
use statime_wire::Message;

fuzz_target!(|data: Vec<u8>| {
// the default maximum size a fuzzed Vec<_> will be, as per
//
// > INFO: -max_len is not provided; libFuzzer will not generate inputs larger
// than 4096 bytes
let mut buf1 = [0u8; 4096];
let mut buf2 = [0u8; 4096];

assert!(data.len() <= buf1.len());

if let Ok(a) = Message::deserialize(&data) {
// verify that the tlv's are parsed without panics
for tlv in a.suffix.tlvs() {
let _ = tlv;
}

let written = a.serialize(&mut buf1).unwrap();
assert!(data.len() >= written);

let b = Message::deserialize(&data).unwrap();
b.serialize(&mut buf2).unwrap();

assert_eq!(buf1, buf2);
}
});
22 changes: 22 additions & 0 deletions statime-wire/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "statime-wire"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
readme.workspace = true
description.workspace = true
publish.workspace = true
rust-version.workspace = true

[features]
default = ["std", "serde"]
std = []
serde = ["dep:serde"]

[dependencies]
serde = { workspace = true, optional = true }

[dev-dependencies]
serde_test.workspace = true
181 changes: 181 additions & 0 deletions statime-wire/src/common/clock_accuracy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
use core::cmp::Ordering;

#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
/// How accurate the underlying clock device is expected to be when not
/// synchronized.
pub enum ClockAccuracy {
/// Reserved
Reserved,
/// Accurate within 1 ps
PS1,
/// Accurate within 2.5 ps
PS2_5,
/// Accurate within 10 ps
PS10,
/// Accurate within 25 ps
PS25,
/// Accurate within 100 ps
PS100,
/// Accurate within 250 ps
PS250,
/// Accurate within 1 ns
NS1,
/// Accurate within 2.5 ns
NS2_5,
/// Accurate within 10 ns
NS10,
/// Accurate within 25 ns
NS25,
/// Accurate within 100 ns
NS100,
/// Accurate within 250 ns
NS250,
/// Accurate within 1 us
US1,
/// Accurate within 2.5 us
US2_5,
/// Accurate within 10 us
US10,
/// Accurate within 25 us
US25,
/// Accurate within 100 us
US100,
/// Accurate within 250 us
US250,
/// Accurate within 1 ms
MS1,
/// Accurate within 2.5 ms
MS2_5,
/// Accurate within 10 ms
MS10,
/// Accurate within 25 ms
MS25,
/// Accurate within 100 ms
MS100,
/// Accurate within 250 ms
MS250,
/// Accurate within 1 s
S1,
/// Accurate within 10 s
S10,
/// Accurate within >10 s
SGT10,
/// Specific to a profile
ProfileSpecific(u8),
/// Accuracy is unknown
#[default]
Unknown,
}

impl ClockAccuracy {
/// Converts enum to u8 literals
#[must_use]
pub(crate) fn to_primitive(self) -> u8 {
match self {
Self::Reserved => 0x00,
Self::PS1 => 0x17,
Self::PS2_5 => 0x18,
Self::PS10 => 0x19,
Self::PS25 => 0x1a,
Self::PS100 => 0x1b,
Self::PS250 => 0x1c,
Self::NS1 => 0x1d,
Self::NS2_5 => 0x1e,
Self::NS10 => 0x1f,
Self::NS25 => 0x20,
Self::NS100 => 0x21,
Self::NS250 => 0x22,
Self::US1 => 0x23,
Self::US2_5 => 0x24,
Self::US10 => 0x25,
Self::US25 => 0x26,
Self::US100 => 0x27,
Self::US250 => 0x28,
Self::MS1 => 0x29,
Self::MS2_5 => 0x2a,
Self::MS10 => 0x2b,
Self::MS25 => 0x2c,
Self::MS100 => 0x2d,
Self::MS250 => 0x2e,
Self::S1 => 0x2f,
Self::S10 => 0x30,
Self::SGT10 => 0x31,
Self::ProfileSpecific(value) => 0x80 + value,
Self::Unknown => 0xfe,
}
}

pub(crate) fn from_primitive(value: u8) -> Self {
match value {
0x00..=0x16 | 0x32..=0x7f | 0xff => Self::Reserved,
0x17 => Self::PS1,
0x18 => Self::PS2_5,
0x19 => Self::PS10,
0x1a => Self::PS25,
0x1b => Self::PS100,
0x1c => Self::PS250,
0x1d => Self::NS1,
0x1e => Self::NS2_5,
0x1f => Self::NS10,
0x20 => Self::NS25,
0x21 => Self::NS100,
0x22 => Self::NS250,
0x23 => Self::US1,
0x24 => Self::US2_5,
0x25 => Self::US10,
0x26 => Self::US25,
0x27 => Self::US100,
0x28 => Self::US250,
0x29 => Self::MS1,
0x2a => Self::MS2_5,
0x2b => Self::MS10,
0x2c => Self::MS25,
0x2d => Self::MS100,
0x2e => Self::MS250,
0x2f => Self::S1,
0x30 => Self::S10,
0x31 => Self::SGT10,
0x80..=0xfd => Self::ProfileSpecific(value - 0x80),
0xfe => ClockAccuracy::Unknown,
}
}
}

impl PartialOrd for ClockAccuracy {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for ClockAccuracy {
fn cmp(&self, other: &Self) -> Ordering {
self.to_primitive().cmp(&other.to_primitive())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn network_protocol_values() {
for i in 0..u8::MAX {
let protocol = ClockAccuracy::from_primitive(i);
if !matches!(protocol, ClockAccuracy::Reserved) {
assert_eq!(protocol.to_primitive(), i);
}
}

assert_eq!(ClockAccuracy::ProfileSpecific(5).to_primitive(), 0x85);
}

#[test]
fn ordering() {
// the inaccuracy of PS1 is less than of PS10
let a = ClockAccuracy::PS1;
let b = ClockAccuracy::PS10;

assert_eq!(a.cmp(&b), Ordering::Less);
}
}
Loading
Loading