srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/fuzz
diff options
context:
space:
mode:
authorsrdusr <[email protected]>2024-05-21 22:24:00 +0200
committersrdusr <[email protected]>2024-05-21 22:24:00 +0200
commitfbedc55d5aa861c381701c9f913b34ee7ab57ec4 (patch)
treed34c3648b61d417f2a5d8690e0eb4a76bd64c943 /fuzz
parente0f4c701028aa81026a17cf9ebfb36112184f4bc (diff)
downloadpacketeer-fbedc55d5aa861c381701c9f913b34ee7ab57ec4.tar.gz
packeteer-fbedc55d5aa861c381701c9f913b34ee7ab57ec4.zip
Add GUI parity for -c/-a, mDNS/SSH dissectors, and two new fuzz harnesses
GUI parity: checksum_status()/reassembled_http_status() moved out of main.cpp into a shared wireframe/packet_diagnostics.hpp so the GUI can show the same -c/-a diagnostics for the selected packet without duplicating the Ethernet/IPv4/TCP walk. Visually verified under Xvfb with the same split-segment scenario used to verify -a on the CLI. Two new L7 dissectors: mDNS (reuses parse_dns outright - RFC 6762 keeps DNS's wire format, just a different port) and SSH's cleartext identification banner. Live-verified against this machine's real sshd and a real DNS-wire-format packet sent to port 5353. Two new fuzz harnesses (fuzz_checksum, fuzz_tcp_reassembly) covering code added here that the original nine harnesses never touched. All 12 run clean across ~90M executions with no crashes. NAMES.md and PLAN.md updated with this round's decisions and naming candidates.
Diffstat (limited to 'fuzz')
-rw-r--r--fuzz/fuzz_checksum.cpp27
-rw-r--r--fuzz/fuzz_tcp_reassembly.cpp42
2 files changed, 69 insertions, 0 deletions
diff --git a/fuzz/fuzz_checksum.cpp b/fuzz/fuzz_checksum.cpp
new file mode 100644
index 0000000..f490d1c
--- /dev/null
+++ b/fuzz/fuzz_checksum.cpp
@@ -0,0 +1,27 @@
+#include <cstddef>
+#include <cstdint>
+
+#include "wireframe/net/checksum.hpp"
+#include "wireframe/net/ipv4.hpp"
+
+using namespace wireframe::net;
+
+// internet_checksum() itself takes arbitrary bytes directly. The
+// verify_*_checksum_ipv4() wrappers additionally need two addresses,
+// so the first 8 bytes of input become src/dst and the rest is treated
+// as the header/segment/datagram under test - exercises the
+// pseudo-header construction (detail::build_ipv4_pseudo_header) along
+// with the checksum math itself.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ internet_checksum({data, size});
+ verify_ipv4_checksum({data, size});
+
+ if (size < 8) return 0;
+ Ipv4Address src{{data[0], data[1], data[2], data[3]}};
+ Ipv4Address dst{{data[4], data[5], data[6], data[7]}};
+ std::span<const unsigned char> rest{data + 8, size - 8};
+
+ verify_tcp_checksum_ipv4(src, dst, rest);
+ verify_udp_checksum_ipv4(src, dst, rest);
+ return 0;
+}
diff --git a/fuzz/fuzz_tcp_reassembly.cpp b/fuzz/fuzz_tcp_reassembly.cpp
new file mode 100644
index 0000000..78ca91c
--- /dev/null
+++ b/fuzz/fuzz_tcp_reassembly.cpp
@@ -0,0 +1,42 @@
+#include <cstddef>
+#include <cstdint>
+
+#include "wireframe/net/tcp_reassembly.hpp"
+
+using namespace wireframe::net;
+
+// Unlike the other fuzz harnesses, this drives *one* TcpReassembler
+// with a whole sequence of segments parsed out of a single input --
+// the interesting bugs here are in cross-call state (the flow map,
+// per-direction sequence tracking, the buffer-size cap), not in
+// decoding one segment alone. Each record is a fixed 19-byte header
+// (src ip/port, dst ip/port, seq, flags, a payload length) followed by
+// that many payload bytes; malformed/truncated trailing records are
+// simply skipped rather than treated as an error, same as any other
+// best-effort parse in this project.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ TcpReassembler reassembler;
+
+ size_t pos = 0;
+ while (pos + 19 <= size) {
+ Ipv4Address src{{data[pos], data[pos + 1], data[pos + 2], data[pos + 3]}};
+ Ipv4Address dst{{data[pos + 4], data[pos + 5], data[pos + 6], data[pos + 7]}};
+ std::uint16_t src_port = static_cast<std::uint16_t>(data[pos + 8] << 8 | data[pos + 9]);
+ std::uint16_t dst_port = static_cast<std::uint16_t>(data[pos + 10] << 8 | data[pos + 11]);
+ std::uint32_t seq = static_cast<std::uint32_t>(data[pos + 12]) << 24 |
+ static_cast<std::uint32_t>(data[pos + 13]) << 16 |
+ static_cast<std::uint32_t>(data[pos + 14]) << 8 | data[pos + 15];
+ std::uint8_t flags = data[pos + 16];
+ std::uint16_t payload_len =
+ static_cast<std::uint16_t>(data[pos + 17] << 8 | data[pos + 18]);
+ pos += 19;
+
+ std::size_t available = size - pos;
+ std::size_t take = payload_len < available ? payload_len : available;
+ std::span<const unsigned char> payload{data + pos, take};
+ pos += take;
+
+ reassembler.process_segment(src, src_port, dst, dst_port, seq, flags, payload);
+ }
+ return 0;
+}