blob: 0a77408b229dd2d535d60500890dcd334f6717f1 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use arbitrary::{Arbitrary, Unstructured};
use fuzz::fuzz;
use fuzz_util::Fuzzed;
use mdns::protocol::Message as MdnsMessage;
use net_types::{
ethernet::Mac,
ip::{Ipv4Addr, Ipv6Addr},
};
use netsvc_proto::{debuglog::DebugLogPacket, netboot::NetbootPacket, tftp::TftpPacket};
use packet::{BufferView, ParsablePacket};
use packet_formats::{
arp::ArpPacket,
ethernet::{EthernetFrame, EthernetFrameLengthCheck},
icmp::{IcmpParseArgs, Icmpv4Packet, Icmpv6Packet},
igmp::messages::IgmpPacket,
ipv4::Ipv4Packet,
ipv6::Ipv6Packet,
tcp::{TcpParseArgs, TcpSegment},
udp::{UdpPacket, UdpParseArgs},
};
use packet_formats_dhcp::v6::Message as Dhcpv6Message;
use tracing::Subscriber;
use tracing_subscriber::{
fmt::{
format::{self, FormatEvent, FormatFields},
FmtContext,
},
registry::LookupSpan,
};
use zerocopy::ByteSlice;
/// Packet formats whose parsers are provided by the [`packet_formats_dhcp`]
/// crate.
#[derive(Arbitrary)]
enum DhcpPacketType {
Dhcpv4Message,
Dhcpv6Message,
}
/// Packet formats whose parsers are provided by the [`mdns`] crate.
#[derive(Arbitrary)]
enum MdnsPacketType {
MdnsMessage,
}
/// Packet formats whose parsers are provided by the [`netsvc_proto`] crate.
#[derive(Arbitrary)]
enum NetsvcPacketType {
DebugLogPacket,
NetbootPacket,
TftpPacket,
}
/// Packet formats whose parsers are provided by the [`ppp_packet`] crate.
#[derive(Arbitrary)]
enum PppPacketType {
ConfigurationPacket,
ControlProtocolPacket,
CodeRejectPacket,
PppPacket,
EchoDiscardPacket,
ProtocolRejectPacket,
TerminationPacket,
}
/// Packet formats whose parsers are provided by the [`packet_formats`] crate.
#[derive(Arbitrary)]
enum PacketFormatsPacketType {
ArpPacket,
Ethernet(Fuzzed<EthernetFrameLengthCheck>),
Icmpv4Packet(Fuzzed<IcmpParseArgs<Ipv4Addr>>),
Icmpv6Packet(Fuzzed<IcmpParseArgs<Ipv6Addr>>),
IgmpPacket,
Ipv4,
Ipv6,
TcpSegmentv4(Fuzzed<TcpParseArgs<Ipv4Addr>>),
TcpSegmentv6(Fuzzed<TcpParseArgs<Ipv6Addr>>),
UdpPacketv4(Fuzzed<UdpParseArgs<Ipv4Addr>>),
UdpPacketv6(Fuzzed<UdpParseArgs<Ipv6Addr>>),
}
trait ParseAndIgnore<A, B: ByteSlice> {
/// Parse the provided value to check for crashes and ignore the result.
fn parse_and_ignore<BV: BufferView<B>>(input: BV, args: A);
}
impl<B, A, T> ParseAndIgnore<A, B> for T
where
B: ByteSlice,
T: ParsablePacket<B, A>,
{
fn parse_and_ignore<BV: BufferView<B>>(input: BV, args: A) {
let _: Result<_, _> = Self::parse(input, args);
}
}
fn init_logging() {
static LOGGER_ONCE: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(true);
if LOGGER_ONCE.swap(false, core::sync::atomic::Ordering::AcqRel) {
struct LogEventFormatter;
impl<S, N> FormatEvent<S, N> for LogEventFormatter
where
S: Subscriber + for<'a> LookupSpan<'a>,
N: for<'a> FormatFields<'a> + 'static,
{
fn format_event(
&self,
ctx: &FmtContext<'_, S, N>,
mut writer: format::Writer<'_>,
event: &tracing::Event<'_>,
) -> std::fmt::Result {
let level = *event.metadata().level();
let path = event.metadata().module_path().unwrap_or("_unknown_");
write!(writer, "[{path}][{level}]: ")?;
ctx.field_format().format_fields(writer.by_ref(), event)?;
writeln!(writer)
}
}
let subscriber = tracing_subscriber::fmt()
.event_format(LogEventFormatter)
.with_writer(std::io::stderr)
.with_max_level(tracing::Level::DEBUG)
.finish();
tracing::subscriber::set_global_default(subscriber).expect("Unable to set global default")
}
}
#[fuzz]
fn fuzz_parse_packet(input: &[u8]) {
init_logging();
let mut unstructured = Unstructured::new(input);
#[derive(Arbitrary)]
enum SupportedPacketType {
Packet(PacketFormatsPacketType),
DhcpPacket(DhcpPacketType),
MdnsPacket(MdnsPacketType),
NetsvcPacket(NetsvcPacketType),
PppPacket(PppPacketType),
}
let parse_as = match unstructured.arbitrary() {
Ok(t) => t,
Err(_) => return,
};
let mut input = unstructured.take_rest();
match parse_as {
SupportedPacketType::Packet(packet_type) => match packet_type {
PacketFormatsPacketType::ArpPacket => {
ArpPacket::<_, Mac, Ipv4Addr>::parse_and_ignore(&mut input, ());
}
PacketFormatsPacketType::Ethernet(args) => {
EthernetFrame::parse_and_ignore(&mut input, args.into());
}
PacketFormatsPacketType::Icmpv4Packet(args) => {
Icmpv4Packet::parse_and_ignore(&mut input, args.into());
}
PacketFormatsPacketType::Icmpv6Packet(args) => {
Icmpv6Packet::parse_and_ignore(&mut input, args.into());
}
PacketFormatsPacketType::IgmpPacket => {
IgmpPacket::parse_and_ignore(&mut input, ());
}
PacketFormatsPacketType::Ipv4 => {
Ipv4Packet::parse_and_ignore(&mut input, ());
}
PacketFormatsPacketType::Ipv6 => {
Ipv6Packet::parse_and_ignore(&mut input, ());
}
PacketFormatsPacketType::TcpSegmentv4(args) => {
TcpSegment::parse_and_ignore(&mut input, args.into());
}
PacketFormatsPacketType::TcpSegmentv6(args) => {
TcpSegment::parse_and_ignore(&mut input, args.into());
}
PacketFormatsPacketType::UdpPacketv4(args) => {
UdpPacket::parse_and_ignore(&mut input, args.into());
}
PacketFormatsPacketType::UdpPacketv6(args) => {
UdpPacket::parse_and_ignore(&mut input, args.into());
}
},
SupportedPacketType::DhcpPacket(dhcp_type) => match dhcp_type {
DhcpPacketType::Dhcpv6Message => {
Dhcpv6Message::parse_and_ignore(&mut input, ());
}
DhcpPacketType::Dhcpv4Message => {
// TODO(https://fxbug.dev/122602): Migrate dhcp_protocol to packet-formats APIs
// and make this use `parse_and_ignore` instead.
let _: Result<dhcp_protocol::Message, dhcp_protocol::ProtocolError> =
dhcp_protocol::Message::from_buffer(input);
}
},
SupportedPacketType::MdnsPacket(mdns_type) => match mdns_type {
MdnsPacketType::MdnsMessage => {
MdnsMessage::parse_and_ignore(&mut input, ());
}
},
SupportedPacketType::NetsvcPacket(netsvc_type) => match netsvc_type {
NetsvcPacketType::DebugLogPacket => {
DebugLogPacket::parse_and_ignore(&mut input, ());
}
NetsvcPacketType::NetbootPacket => {
NetbootPacket::parse_and_ignore(&mut input, ());
}
NetsvcPacketType::TftpPacket => {
TftpPacket::parse_and_ignore(&mut input, ());
}
},
SupportedPacketType::PppPacket(ppp_type) => {
use ppp_packet::{
CodeRejectPacket, ConfigurationPacket, ControlProtocolPacket, EchoDiscardPacket,
PppPacket, ProtocolRejectPacket, TerminationPacket,
};
match ppp_type {
PppPacketType::ConfigurationPacket => {
ConfigurationPacket::parse_and_ignore(&mut input, ());
}
PppPacketType::CodeRejectPacket => {
CodeRejectPacket::parse_and_ignore(&mut input, ());
}
PppPacketType::ControlProtocolPacket => {
ControlProtocolPacket::parse_and_ignore(&mut input, ());
}
PppPacketType::EchoDiscardPacket => {
EchoDiscardPacket::parse_and_ignore(&mut input, ());
}
PppPacketType::PppPacket => {
PppPacket::parse_and_ignore(&mut input, ());
}
PppPacketType::ProtocolRejectPacket => {
ProtocolRejectPacket::parse_and_ignore(&mut input, ());
}
PppPacketType::TerminationPacket => {
TerminationPacket::parse_and_ignore(&mut input, ());
}
}
}
}
}