Add fuzz tests for 6LoWPAN_IPHC and 6LoWPAN_UDP
Adding fuzz tests for 6LoWPAN_IPHC and 6LoWPAN_UDP. Some bugs were found. Ran for 10 minutes.master
parent
e8659d7cca
commit
a9925b5957
|
@ -10,6 +10,7 @@ cargo-fuzz = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libfuzzer-sys = "0.4"
|
libfuzzer-sys = "0.4"
|
||||||
|
arbitrary = { version = "1", features = ["derive"] }
|
||||||
getopts = "0.2"
|
getopts = "0.2"
|
||||||
smoltcp = { path = "..", features = [ "medium-ethernet" ] }
|
smoltcp = { path = "..", features = [ "medium-ethernet" ] }
|
||||||
|
|
||||||
|
@ -40,3 +41,15 @@ name = "ieee802154_header"
|
||||||
path = "fuzz_targets/ieee802154_header.rs"
|
path = "fuzz_targets/ieee802154_header.rs"
|
||||||
test = false
|
test = false
|
||||||
doc = 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
|
||||||
|
|
|
@ -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<AddressFuzzer> 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<AddressFuzzer>,
|
||||||
|
ll_dst_addr: Option<AddressFuzzer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
|
@ -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<AddressFuzzer> 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<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
|
@ -128,10 +128,21 @@ pub mod iphc {
|
||||||
pub fn check_len(&self) -> Result<()> {
|
pub fn check_len(&self) -> Result<()> {
|
||||||
let buffer = self.buffer.as_ref();
|
let buffer = self.buffer.as_ref();
|
||||||
if buffer.len() < 2 {
|
if buffer.len() < 2 {
|
||||||
Err(Error::Truncated)
|
return Err(Error::Truncated);
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
/// Consumes the frame, returning the underlying buffer.
|
||||||
|
@ -797,6 +808,12 @@ pub mod iphc {
|
||||||
1 // The next header field is inlined
|
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
|
// Add the lenght of the source address
|
||||||
len += if self.src_addr == ipv6::Address::UNSPECIFIED {
|
len += if self.src_addr == ipv6::Address::UNSPECIFIED {
|
||||||
0
|
0
|
||||||
|
@ -1279,11 +1296,17 @@ pub mod nhc {
|
||||||
/// Returns `Err(Error::Truncated)` if the buffer is too short.
|
/// Returns `Err(Error::Truncated)` if the buffer is too short.
|
||||||
pub fn check_len(&self) -> Result<()> {
|
pub fn check_len(&self) -> Result<()> {
|
||||||
let buffer = self.buffer.as_ref();
|
let buffer = self.buffer.as_ref();
|
||||||
|
|
||||||
if buffer.is_empty() {
|
if buffer.is_empty() {
|
||||||
Err(Error::Truncated)
|
return Err(Error::Truncated);
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
/// Consumes the frame, returning the underlying buffer.
|
||||||
|
@ -1357,7 +1380,7 @@ pub mod nhc {
|
||||||
let data = self.buffer.as_ref();
|
let data = self.buffer.as_ref();
|
||||||
let start = self.nhc_fields_start();
|
let start = self.nhc_fields_start();
|
||||||
|
|
||||||
0xf0b0 + (NetworkEndian::read_u16(&data[start..start + 1]) & 0xff)
|
0xf0b0 + (data[start] & 0xff) as u16
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -1501,10 +1524,14 @@ pub mod nhc {
|
||||||
checksum::data(packet.payload()),
|
checksum::data(packet.payload()),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// TODO(thvdveld): remove the unwrap
|
if let Some(checksum) = packet.checksum() {
|
||||||
if chk_sum != packet.checksum().unwrap() {
|
if chk_sum != checksum {
|
||||||
return Err(Error::Checksum);
|
return Err(Error::Checksum);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
net_trace!("Currently we do not support ellided checksums.");
|
||||||
|
return Err(Error::Unrecognized);
|
||||||
|
};
|
||||||
|
|
||||||
Ok(UdpNhcRepr(UdpRepr {
|
Ok(UdpNhcRepr(UdpRepr {
|
||||||
src_port: packet.src_port(),
|
src_port: packet.src_port(),
|
||||||
|
|
Loading…
Reference in New Issue