1 #![no_main]
2 use libfuzzer_sys::fuzz_target;
3 use smoltcp::iface::{InterfaceBuilder, NeighborCache};
4 use smoltcp::phy::{Loopback, Medium};
5 use smoltcp::socket::tcp;
6 use smoltcp::time::{Duration, Instant};
7 use smoltcp::wire::{EthernetAddress, EthernetFrame, EthernetProtocol};
8 use smoltcp::wire::{IpAddress, IpCidr, Ipv4Packet, Ipv6Packet, TcpPacket};
9 use std::cmp;
10 
11 #[path = "../utils.rs"]
12 mod utils;
13 
14 mod mock {
15     use smoltcp::time::{Duration, Instant};
16     use std::sync::atomic::{AtomicUsize, Ordering};
17     use std::sync::Arc;
18 
19     // should be AtomicU64 but that's unstable
20     #[derive(Debug, Clone)]
21     #[cfg_attr(feature = "defmt", derive(defmt::Format))]
22     pub struct Clock(Arc<AtomicUsize>);
23 
24     impl Clock {
new() -> Clock25         pub fn new() -> Clock {
26             Clock(Arc::new(AtomicUsize::new(0)))
27         }
28 
advance(&self, duration: Duration)29         pub fn advance(&self, duration: Duration) {
30             self.0
31                 .fetch_add(duration.total_millis() as usize, Ordering::SeqCst);
32         }
33 
elapsed(&self) -> Instant34         pub fn elapsed(&self) -> Instant {
35             Instant::from_millis(self.0.load(Ordering::SeqCst) as i64)
36         }
37     }
38 }
39 
40 struct TcpHeaderFuzzer([u8; 56], usize);
41 
42 impl TcpHeaderFuzzer {
43     // The fuzzer won't fuzz any packets with the SYN flag set in order to make sure the connection
44     // is established before the fuzzed headers arrive.
45     //
46     // It will also not fuzz the source and dest port so it reaches the open socket.
47     //
48     // Otherwise, it replaces the entire rest of the TCP header with the fuzzer's output.
new(data: &[u8]) -> TcpHeaderFuzzer49     pub fn new(data: &[u8]) -> TcpHeaderFuzzer {
50         let copy_len = cmp::min(
51             data.len(),
52             56, /* max TCP header length without port numbers*/
53         );
54 
55         let mut fuzzer = TcpHeaderFuzzer([0; 56], copy_len);
56         fuzzer.0[..copy_len].copy_from_slice(&data[..copy_len]);
57         fuzzer
58     }
59 }
60 
61 impl smoltcp::phy::Fuzzer for TcpHeaderFuzzer {
fuzz_packet(&self, frame_data: &mut [u8])62     fn fuzz_packet(&self, frame_data: &mut [u8]) {
63         if self.1 == 0 {
64             return;
65         }
66 
67         let tcp_packet_offset = {
68             let eth_frame = EthernetFrame::new_unchecked(&frame_data);
69             EthernetFrame::<&mut [u8]>::header_len()
70                 + match eth_frame.ethertype() {
71                     EthernetProtocol::Ipv4 => {
72                         Ipv4Packet::new_unchecked(eth_frame.payload()).header_len() as usize
73                     }
74                     EthernetProtocol::Ipv6 => {
75                         Ipv6Packet::new_unchecked(eth_frame.payload()).header_len() as usize
76                     }
77                     _ => return,
78                 }
79         };
80 
81         let tcp_is_syn = {
82             let tcp_packet = TcpPacket::new_checked(&frame_data[tcp_packet_offset..]).unwrap();
83             tcp_packet.syn()
84         };
85 
86         if tcp_is_syn {
87             return;
88         }
89 
90         if !frame_data.ends_with(b"abcdef") {
91             return;
92         }
93 
94         let tcp_header_len = {
95             let tcp_packet = &frame_data[tcp_packet_offset..];
96             (tcp_packet[12] as usize >> 4) * 4
97         };
98 
99         let tcp_packet = &mut frame_data[tcp_packet_offset + 4..];
100 
101         let replacement_data = &self.0[..self.1];
102         let copy_len = cmp::min(replacement_data.len(), tcp_header_len);
103         assert!(copy_len < tcp_packet.len());
104         tcp_packet[..copy_len].copy_from_slice(&replacement_data[..copy_len]);
105     }
106 }
107 
108 struct EmptyFuzzer();
109 
110 impl smoltcp::phy::Fuzzer for EmptyFuzzer {
fuzz_packet(&self, _: &mut [u8])111     fn fuzz_packet(&self, _: &mut [u8]) {}
112 }
113 
114 fuzz_target!(|data: &[u8]| {
115     let clock = mock::Clock::new();
116 
117     let device = {
118         let (mut opts, mut free) = utils::create_options();
119         utils::add_middleware_options(&mut opts, &mut free);
120 
121         let mut matches = utils::parse_options(&opts, free);
122         let device = utils::parse_middleware_options(
123             &mut matches,
124             Loopback::new(Medium::Ethernet),
125             /*loopback=*/ true,
126         );
127 
128         smoltcp::phy::FuzzInjector::new(device, EmptyFuzzer(), TcpHeaderFuzzer::new(data))
129     };
130 
131     let mut neighbor_cache_entries = [None; 8];
132     let neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]);
133 
134     let ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)];
135     let mut iface = InterfaceBuilder::new()
136         .ethernet_addr(EthernetAddress::default())
137         .neighbor_cache(neighbor_cache)
138         .ip_addrs(ip_addrs)
139         .finalize(&mut device);
140 
141     let server_socket = {
142         // It is not strictly necessary to use a `static mut` and unsafe code here, but
143         // on embedded systems that smoltcp targets it is far better to allocate the data
144         // statically to verify that it fits into RAM rather than get undefined behavior
145         // when stack overflows.
146         static mut TCP_SERVER_RX_DATA: [u8; 1024] = [0; 1024];
147         static mut TCP_SERVER_TX_DATA: [u8; 1024] = [0; 1024];
148         let tcp_rx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] });
149         let tcp_tx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] });
150         tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer)
151     };
152 
153     let client_socket = {
154         static mut TCP_CLIENT_RX_DATA: [u8; 1024] = [0; 1024];
155         static mut TCP_CLIENT_TX_DATA: [u8; 1024] = [0; 1024];
156         let tcp_rx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_CLIENT_RX_DATA[..] });
157         let tcp_tx_buffer = tcp::SocketBuffer::new(unsafe { &mut TCP_CLIENT_TX_DATA[..] });
158         tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer)
159     };
160 
161     let mut socket_set_entries: [_; 2] = Default::default();
162     let mut socket_set = SocketSet::new(&mut socket_set_entries[..]);
163     let server_handle = socket_set.add(server_socket);
164     let client_handle = socket_set.add(client_socket);
165 
166     let mut did_listen = false;
167     let mut did_connect = false;
168     let mut done = false;
169     while !done && clock.elapsed() < Instant::from_millis(4_000) {
170         let _ = iface.poll(&mut socket_set, clock.elapsed());
171 
172         {
173             let mut socket = socket_set.get::<tcp::Socket>(server_handle);
174             if !socket.is_active() && !socket.is_listening() {
175                 if !did_listen {
176                     socket.listen(1234).unwrap();
177                     did_listen = true;
178                 }
179             }
180 
181             if socket.can_recv() {
182                 socket.close();
183                 done = true;
184             }
185         }
186 
187         {
188             let mut socket = socket_set.get::<tcp::Socket>(client_handle);
189             if !socket.is_open() {
190                 if !did_connect {
191                     socket
192                         .connect(
193                             (IpAddress::v4(127, 0, 0, 1), 1234),
194                             (IpAddress::Unspecified, 65000),
195                         )
196                         .unwrap();
197                     did_connect = true;
198                 }
199             }
200 
201             if socket.can_send() {
202                 socket
203                     .send_slice(b"0123456789abcdef0123456789abcdef0123456789abcdef")
204                     .unwrap();
205                 socket.close();
206             }
207         }
208 
209         match iface.poll_delay(&socket_set, clock.elapsed()) {
210             Some(Duration::ZERO) => {}
211             Some(delay) => clock.advance(delay),
212             None => clock.advance(Duration::from_millis(1)),
213         }
214     }
215 });
216