Skip to content

Commit

Permalink
Merge pull request #138 from martinling/crc
Browse files Browse the repository at this point in the history
Validate packet CRCs and lengths, and diagnose malformed packets
  • Loading branch information
martinling authored Jul 23, 2024
2 parents 2380eac + 2850870 commit 63b7282
Show file tree
Hide file tree
Showing 11 changed files with 2,318 additions and 102 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ lrumap = "0.1.0"
memmap2 = "0.9.4"
page_size = "0.6.0"
anyhow = { version = "1.0.79", features = ["backtrace"] }
crc = "3.2.1"

[dev-dependencies]
serde = { version = "1.0.196", features = ["derive"] }
Expand Down
142 changes: 100 additions & 42 deletions src/capture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::data_stream::{
use crate::compact_index::{compact_index, CompactWriter, CompactReader};
use crate::rcu::SingleWriterRcu;
use crate::vec_map::VecMap;
use crate::usb::{self, prelude::*};
use crate::usb::{self, prelude::*, validate_packet};
use crate::util::{fmt_count, fmt_size};

use anyhow::{Context, Error, bail};
Expand Down Expand Up @@ -596,8 +596,6 @@ impl Transaction {
Ok(match (self.start_pid, &self.split) {
(SOF, _) => format!(
"{} SOF packets", self.packet_count()),
(Malformed, _) => format!(
"{} malformed packets", self.packet_count()),
(SPLIT, Some((split_fields, token_pid))) => format!(
"{} {}",
match split_fields.sc() {
Expand Down Expand Up @@ -799,7 +797,7 @@ impl CaptureReader {
match data_packet.first() {
None => bail!("Found empty packet instead of setup data"),
Some(byte) => {
let pid = PID::from(*byte);
let pid = PID::from(byte);
if pid != PID::DATA0 {
bail!("Found {pid} packet instead of setup data")
} else if data_packet.len() != 11 {
Expand Down Expand Up @@ -1150,55 +1148,115 @@ impl ItemSource<TrafficItem> for CaptureReader {
fn summary(&mut self, item: &TrafficItem)
-> Result<String, Error>
{
use PID::*;
use TrafficItem::*;
use usb::StartComplete::*;
Ok(match item {
Packet(.., packet_id) => {
let packet = self.packet(*packet_id)?;
let first_byte = *packet.first().with_context(|| format!(
"Packet {packet_id} is empty, cannot retrieve PID"))?;
let pid = PID::from(first_byte);
format!("{pid} packet{}",
match PacketFields::from_packet(&packet) {
PacketFields::SOF(sof) => format!(
" with frame number {}, CRC {:02X}",
sof.frame_number(),
sof.crc()),
PacketFields::Token(token) => format!(
" on {}.{}, CRC {:02X}",
token.device_address(),
token.endpoint_number(),
token.crc()),
PacketFields::Data(data) if packet.len() <= 3 => format!(
" with CRC {:04X} and no data",
data.crc),
PacketFields::Data(data) => format!(
" with CRC {:04X} and {} data bytes: {}",
data.crc,
packet.len() - 3,
Bytes::first(100, &packet[1 .. packet.len() - 2])),
PacketFields::Split(split) => format!(
" {} {} speed {} transaction on hub {} port {}",
match split.sc() {
Start => "starting",
Complete => "completing",
},
format!("{:?}", split.speed()).to_lowercase(),
format!("{:?}", split.endpoint_type()).to_lowercase(),
split.hub_address(),
split.port()),
PacketFields::None => match pid {
PID::Malformed => format!(": {packet:02X?}"),
_ => "".to_string()
let len = packet.len();
let too_long = len > 1027;
match validate_packet(&packet) {
Err(None) => "Malformed 0-byte packet".to_string(),
Err(Some(pid)) => format!(
"Malformed packet{} of {len} {}: {}",
match pid {
RSVD if too_long =>
" (reserved PID, and too long)".to_string(),
Malformed if too_long =>
" (invalid PID, and too long)".to_string(),
RSVD =>
" (reserved PID)".to_string(),
Malformed =>
" (invalid PID)".to_string(),
pid if too_long => format!(
" (possibly {pid}, but too long)"),
pid => format!(
" (possibly {pid}, but {})",
match pid {
SOF|SETUP|IN|OUT|PING => {
if len != 3 {
"wrong length"
} else {
"bad CRC"
}
},
SPLIT => {
if len != 4 {
"wrong length"
} else {
"bad CRC"
}
},
DATA0|DATA1|DATA2|MDATA => {
if len < 3 {
"too short"
} else {
"bad CRC"
}
},
ACK|NAK|NYET|STALL|ERR => "too long",
RSVD|Malformed => unreachable!(),
}),
},
if len == 1 {"byte"} else {"bytes"},
Bytes::first(100, &packet[0 .. len])
),
Ok(pid) => format!(
"{pid} packet{}",
match PacketFields::from_packet(&packet) {
PacketFields::SOF(sof) => format!(
" with frame number {}, CRC {:02X}",
sof.frame_number(),
sof.crc()),
PacketFields::Token(token) => format!(
" on {}.{}, CRC {:02X}",
token.device_address(),
token.endpoint_number(),
token.crc()),
PacketFields::Data(data) if len <= 3 => format!(
" with CRC {:04X} and no data",
data.crc),
PacketFields::Data(data) => format!(
" with CRC {:04X} and {} data bytes: {}",
data.crc,
len - 3,
Bytes::first(100, &packet[1 .. len - 2])),
PacketFields::Split(split) => format!(
" {} {} speed {} transaction on hub {} port {}",
match split.sc() {
Start => "starting",
Complete => "completing",
},
format!("{:?}", split.speed()).to_lowercase(),
format!("{:?}", split.endpoint_type()).to_lowercase(),
split.hub_address(),
split.port()),
PacketFields::None => match pid {
PID::Malformed => format!(": {packet:02X?}"),
_ => "".to_string()
}
}
})
)
}
},
Transaction(transfer_id, transaction_id) => {
let entry = self.transfer_index.get(*transfer_id)?;
let endpoint_id = entry.endpoint_id();
let endpoint = self.endpoints.get(endpoint_id)?;
let transaction = self.transaction(*transaction_id)?;
transaction.description(self, &endpoint)?
let packet_id_range = self.transaction_index.target_range(
*transaction_id, self.packet_index.len())?;
let start_packet_id = packet_id_range.start;
let start_packet = self.packet(start_packet_id)?;
if validate_packet(&start_packet).is_ok() {
let transaction = self.transaction(*transaction_id)?;
transaction.description(self, &endpoint)?
} else {
let packet_count = packet_id_range.len();
format!("{} malformed {}",
packet_count,
if packet_count == 1 {"packet"} else {"packets"})
}
},
Transfer(transfer_id) => {
use EndpointType::*;
Expand Down
58 changes: 28 additions & 30 deletions src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,9 @@ use anyhow::{Context, Error, bail};

use crate::capture::prelude::*;
use crate::rcu::SingleWriterRcu;
use crate::usb::{self, prelude::*};
use crate::usb::{self, prelude::*, validate_packet};
use crate::vec_map::{VecMap, Key};

impl PID {
fn from_packet(packet: &[u8]) -> Result<PID, Error> {
let first_byte = packet
.first()
.context("Packet is empty, cannot retrieve PID")?;
Ok(PID::from(*first_byte))
}
}

struct EndpointData {
device_id: DeviceId,
address: EndpointAddr,
Expand Down Expand Up @@ -95,15 +86,20 @@ struct TransactionState {
}

fn transaction_status(state: &Option<TransactionState>, packet: &[u8])
-> Result<TransactionStatus, Error>
-> Result<(PID, TransactionStatus), Error>
{
let next = PID::from_packet(packet)?;
use PID::*;
use TransactionStatus::*;
use TransactionStyle::*;
use StartComplete::*;
use usb::EndpointType::*;
Ok(match state {

let next = match validate_packet(packet) {
Err(_) => return Ok((Malformed, Invalid)),
Ok(pid) => pid,
};

let status = match state {
None => match next {
// Tokens may start a new transaction.
SOF | SETUP | IN | OUT | PING | SPLIT => New,
Expand Down Expand Up @@ -219,7 +215,9 @@ fn transaction_status(state: &Option<TransactionState>, packet: &[u8])
(..) => Invalid,
}
},
})
};

Ok((next, status))
}

impl TransactionState {
Expand All @@ -235,12 +233,12 @@ impl TransactionState {
self.endpoint_id.context("Transaction state has no endpoint ID")
}

fn extract_payload(&mut self, packet: &[u8]) {
fn extract_payload(&mut self, pid: PID, packet: &[u8]) {
use PID::*;
use TransactionStyle::*;
use usb::EndpointType::*;
use StartComplete::*;
match (&self.style, PID::from(packet[0])) {
match (&self.style, pid) {
(Simple(SETUP), DATA0) |
(Split(Start, Control, Some(SETUP)), DATA0) => {
self.setup = Some(SetupFields::from_data_packet(packet));
Expand Down Expand Up @@ -622,10 +620,9 @@ impl Decoder {
})
}

fn packet_endpoint(&mut self, packet: &[u8])
fn packet_endpoint(&mut self, pid: PID, packet: &[u8])
-> Result<EndpointId, Error>
{
let pid = PID::from_packet(packet)?;
Ok(match PacketFields::from_packet(packet) {
PacketFields::SOF(_) => FRAMING_EP_ID,
PacketFields::Token(token) =>
Expand All @@ -640,7 +637,7 @@ impl Decoder {
use TransactionStatus::*;
use TransactionStyle::*;
use StartComplete::*;
let status = transaction_status(&self.transaction_state, packet)?;
let (pid, status) = transaction_status(&self.transaction_state, packet)?;
let success = status != Fail;
let complete = match &self.transaction_state {
None => false,
Expand All @@ -651,43 +648,45 @@ impl Decoder {
};
if status != Invalid {
if let Some(state) = &mut self.transaction_state {
state.extract_payload(packet);
state.extract_payload(pid, packet);
}
}
match status {
New => {
self.transaction_end(false, false)?;
self.transaction_start(packet_id, packet)?;
self.transaction_start(packet_id, pid, packet)?;
self.transfer_early_append()?;
},
Continue => {
self.transaction_append(packet)?;
self.transaction_append(pid, packet)?;
self.transfer_early_append()?;
},
Done | Retry | Fail => {
self.transaction_append(packet)?;
self.transaction_append(pid, packet)?;
self.transaction_end(success, complete)?;
},
Invalid => {
self.transaction_start(packet_id, packet)?;
self.transaction_start(packet_id, pid, packet)?;
self.transaction_end(false, false)?;
},
};
Ok(())
}

fn transaction_start(&mut self, packet_id: PacketId, packet: &[u8])
fn transaction_start(&mut self,
packet_id: PacketId,
pid: PID,
packet: &[u8])
-> Result<(), Error>
{
use PID::*;
use TransactionStyle::*;
let pid = PID::from_packet(packet)?;
let transaction_id = self.capture.transaction_index.push(packet_id)?;
let (style, endpoint_id) = if pid == SPLIT {
let split = SplitFields::from_packet(packet);
(Split(split.sc(), split.endpoint_type(), None), None)
} else {
(Simple(pid), Some(self.packet_endpoint(packet)?))
(Simple(pid), Some(self.packet_endpoint(pid, packet)?))
};
let mut state = TransactionState {
style,
Expand All @@ -704,15 +703,14 @@ impl Decoder {
Ok(())
}

fn transaction_append(&mut self, packet: &[u8])
fn transaction_append(&mut self, pid: PID, packet: &[u8])
-> Result<(), Error>
{
use TransactionStyle::*;
let pid = PID::from_packet(packet)?;
let update = match &self.transaction_state {
Some(TransactionState { style: Split(sc, ep_type, None), ..}) => {
let (sc, ep_type) = (*sc, *ep_type);
let endpoint_id = self.packet_endpoint(packet)?;
let endpoint_id = self.packet_endpoint(pid, packet)?;
let ep_data = &self.endpoint_data[endpoint_id];
let ep_addr = ep_data.address;
let dev_data = self.capture.device_data(ep_data.device_id)?;
Expand Down
Loading

0 comments on commit 63b7282

Please sign in to comment.