Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate packet CRCs and lengths, and diagnose malformed packets #138

Merged
merged 8 commits into from
Jul 23, 2024
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
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