Browse Source

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.
Thibaut Vandervelden 3 years ago
parent
commit
b540ec30b4

+ 13 - 0
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

+ 42 - 0
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<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);
+        }
+    };
+});

+ 43 - 0
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<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),
+            );
+        }
+    };
+});

+ 38 - 11
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(),