diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 8cbb5db..98bd4d0 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -10,6 +10,7 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" +arbitrary = { version = "1", features = ["derive"] } getopts = "0.2" smoltcp = { path = "..", features = [ "medium-ethernet" ] } @@ -40,3 +41,15 @@ name = "ieee802154_header" path = "fuzz_targets/ieee802154_header.rs" test = false doc = false + +[[bin]] +name = "sixlowpan_udp_header" +path = "fuzz_targets/sixlowpan_udp_header.rs" +test = false +doc = false + +[[bin]] +name = "sixlowpan_iphc_header" +path = "fuzz_targets/sixlowpan_iphc_header.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/sixlowpan_iphc_header.rs b/fuzz/fuzz_targets/sixlowpan_iphc_header.rs new file mode 100644 index 0000000..3ed012b --- /dev/null +++ b/fuzz/fuzz_targets/sixlowpan_iphc_header.rs @@ -0,0 +1,42 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; +use smoltcp::wire::{Ieee802154Address, SixlowpanIphcPacket, SixlowpanIphcRepr}; + +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)] +pub enum AddressFuzzer { + Absent, + Short([u8; 2]), + Extended([u8; 8]), +} + +impl From for Ieee802154Address { + fn from(val: AddressFuzzer) -> Self { + match val { + AddressFuzzer::Absent => Ieee802154Address::Absent, + AddressFuzzer::Short(b) => Ieee802154Address::Short(b), + AddressFuzzer::Extended(b) => Ieee802154Address::Extended(b), + } + } +} + +#[derive(Debug, arbitrary::Arbitrary)] +struct SixlowpanIphcPacketFuzzer<'a> { + data: &'a [u8], + ll_src_addr: Option, + ll_dst_addr: Option, +} + +fuzz_target!(|fuzz: SixlowpanIphcPacketFuzzer| { + if let Ok(ref frame) = SixlowpanIphcPacket::new_checked(fuzz.data) { + if let Ok(repr) = SixlowpanIphcRepr::parse( + frame, + fuzz.ll_src_addr.map(Into::into), + fuzz.ll_dst_addr.map(Into::into), + ) { + let mut buffer = vec![0; repr.buffer_len()]; + + let mut frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]); + repr.emit(&mut frame); + } + }; +}); diff --git a/fuzz/fuzz_targets/sixlowpan_udp_header.rs b/fuzz/fuzz_targets/sixlowpan_udp_header.rs new file mode 100644 index 0000000..9a2079b --- /dev/null +++ b/fuzz/fuzz_targets/sixlowpan_udp_header.rs @@ -0,0 +1,43 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; +use smoltcp::wire::{Ipv6Address, SixlowpanUdpPacket, SixlowpanUdpRepr}; + +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)] +pub struct AddressFuzzer(pub [u8; 16]); + +impl From for Ipv6Address { + fn from(val: AddressFuzzer) -> Self { + Ipv6Address(val.0) + } +} + +#[derive(Debug, arbitrary::Arbitrary)] +struct SixlowpanUdpPacketFuzzer<'a> { + data: &'a [u8], + src_addr: AddressFuzzer, + dst_addr: AddressFuzzer, + checksum: Option, +} + +fuzz_target!(|fuzz: SixlowpanUdpPacketFuzzer| { + if let Ok(ref frame) = SixlowpanUdpPacket::new_checked(fuzz.data) { + if let Ok(repr) = SixlowpanUdpRepr::parse( + frame, + &fuzz.src_addr.into(), + &fuzz.dst_addr.into(), + fuzz.checksum, + ) { + let payload = frame.payload(); + let mut buffer = vec![0; repr.header_len() + payload.len()]; + + let mut frame = SixlowpanUdpPacket::new_unchecked(&mut buffer[..]); + repr.emit( + &mut frame, + &fuzz.src_addr.into(), + &fuzz.dst_addr.into(), + payload.len(), + |b| b.copy_from_slice(payload), + ); + } + }; +}); diff --git a/src/wire/sixlowpan.rs b/src/wire/sixlowpan.rs index 3ca55ab..ae605f7 100644 --- a/src/wire/sixlowpan.rs +++ b/src/wire/sixlowpan.rs @@ -128,10 +128,21 @@ pub mod iphc { pub fn check_len(&self) -> Result<()> { let buffer = self.buffer.as_ref(); if buffer.len() < 2 { - Err(Error::Truncated) - } else { - Ok(()) + return Err(Error::Truncated); } + + let mut offset = self.ip_fields_start() + + self.traffic_class_size() + + self.next_header_size() + + self.hop_limit_size(); + offset += self.src_address_size(); + offset += self.dst_address_size(); + + if offset as usize > buffer.len() { + return Err(Error::Truncated); + } + + Ok(()) } /// Consumes the frame, returning the underlying buffer. @@ -797,6 +808,12 @@ pub mod iphc { 1 // The next header field is inlined }; + // Hop Limit size + len += match self.hop_limit { + 255 | 64 | 1 => 0, // We can inline the hop limit + _ => 1, + }; + // Add the lenght of the source address len += if self.src_addr == ipv6::Address::UNSPECIFIED { 0 @@ -1279,11 +1296,17 @@ pub mod nhc { /// Returns `Err(Error::Truncated)` if the buffer is too short. pub fn check_len(&self) -> Result<()> { let buffer = self.buffer.as_ref(); + if buffer.is_empty() { - Err(Error::Truncated) - } else { - Ok(()) + return Err(Error::Truncated); } + + let index = 1 + self.ports_size() + self.checksum_size(); + if index > buffer.len() { + return Err(Error::Truncated); + } + + Ok(()) } /// Consumes the frame, returning the underlying buffer. @@ -1357,7 +1380,7 @@ pub mod nhc { let data = self.buffer.as_ref(); let start = self.nhc_fields_start(); - 0xf0b0 + (NetworkEndian::read_u16(&data[start..start + 1]) & 0xff) + 0xf0b0 + (data[start] & 0xff) as u16 } _ => unreachable!(), } @@ -1501,10 +1524,14 @@ pub mod nhc { checksum::data(packet.payload()), ]); - // TODO(thvdveld): remove the unwrap - if chk_sum != packet.checksum().unwrap() { - return Err(Error::Checksum); - } + if let Some(checksum) = packet.checksum() { + if chk_sum != checksum { + return Err(Error::Checksum); + } + } else { + net_trace!("Currently we do not support ellided checksums."); + return Err(Error::Unrecognized); + }; Ok(UdpNhcRepr(UdpRepr { src_port: packet.src_port(),