public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: David Gibson <david@gibson.dropbear.id.au>
To: Anshu Kumari <anskuma@redhat.com>
Cc: sbrivio@redhat.com, passt-dev@passt.top, aerosound161@gmail.com,
	abdobngad@gmail.com, lvivier@redhat.com
Subject: Re: [PATCH 5/5] fuzz: Add test server for bidirectional protocol fuzzing
Date: Fri, 14 Aug 2026 15:50:27 +1000	[thread overview]
Message-ID: <an6slIfmlBj0mRns@zatzit> (raw)
In-Reply-To: <20260812072630.3235261-6-anskuma@redhat.com>

[-- Attachment #1: Type: text/plain, Size: 20350 bytes --]

On Wed, Aug 12, 2026 at 12:56:28PM +0530, Anshu Kumari wrote:
> Add fuzz-server that acts as passt's network peer
> during fuzzing. It connects to passt's UNIX socket
> and listens on 127.0.0.1:9999 for TCP connections.

As discussed in my other reply, I'm not really convinced a test server
makes sense at all, at least in this form.  So, I won't have much to
say on the details here.

> UNIX socket path: responds to ARP requests and TCP SYNs with
>   stateless replies (swapped addresses, fixed ISN). Responses
>   are XOR'd with AFL++ shared memory data so the fuzzer can
>   mutate server behavior.

What's the reason for having a fixed response that's then mutated,
rather than just generating the response directly from AFL's data?

> TCP loopback path: accepts connections on port 9999, reads
>   data, XOR-mutates with AFL++ shared memory, and echoes it
>   back. Uses SO_LINGER(0) for immediate RST on close to
>   avoid TIME_WAIT port exhaustion.
> 
> Turn-based synchronization via mmap'd flag in /dev/shm
>   coordinates frame exchange between passt and the server.
> 
> Also adds:
> - fuzzing/testcase_dir/empty.bin
> - fuzzing/README.fuzzing.md
> 
> Signed-off-by: Anshu Kumari <anskuma@redhat.com>
> ---
>  Makefile                       |   8 +-
>  fuzz-server.c                  | 490 +++++++++++++++++++++++++++++++++
>  fuzzing/README.fuzzing.md      |  95 +++++++
>  fuzzing/testcase_dir/empty.bin | Bin 0 -> 12 bytes
>  4 files changed, 591 insertions(+), 2 deletions(-)
>  create mode 100644 fuzz-server.c
>  create mode 100644 fuzzing/README.fuzzing.md
>  create mode 100644 fuzzing/testcase_dir/empty.bin
> 
> diff --git a/Makefile b/Makefile
> index 8e4121e..4517bc7 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -125,17 +125,21 @@ valgrind: all
>  
>  FUZZ_CC ?= afl-clang-fast
>  
> -.PHONY: fuzz
> +.PHONY: fuzz fuzz-server
>  
>  fuzz:
>  	$(MAKE) clean
>  	$(MAKE) CC="$(FUZZ_CC)" CPPFLAGS="-DFUZZING -DNDEBUG" CFLAGS="-g -fsanitize=address" passt

Oh, sorry, forgot to mention this on the earlier patch: compiling with
-DNDEBUG for fuzzing seems undesirable.  Generally if fuzzed input can
cause an assert() that's a bug, which we'd like to catch.  -NDEBUG
will suppress the assert()s, so we won't see it.

> +fuzz-server:
> +	$(CC) -D_GNU_SOURCE -O2 -o fuzz-server fuzz-server.c
> +
>  .PHONY: clean
>  clean:
>  	$(RM) $(BIN) *~ *.o seccomp.h seccomp_repair.h seccomp_pesto.h pasta.1 \
>  		passt.tar passt.tar.gz *.deb *.rpm \
> -		passt.pid README.plain.md
> +		passt.pid README.plain.md \
> +		fuzz-server
>  
>  install: $(BIN) $(MANPAGES) docs
>  	mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir)
> diff --git a/fuzz-server.c b/fuzz-server.c
> new file mode 100644
> index 0000000..c9b4101
> --- /dev/null
> +++ b/fuzz-server.c
> @@ -0,0 +1,490 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +
> +/* fuzz-server.c - Test server for bidirectional fuzz testing of passt
> + *
> + * Connects to passt's UNIX socket as a client.
> + * Reads outbound frames, generates protocol responses, XORs them
> + * with AFL++ shared memory data, and writes them back.
> + *
> + * Build: make fuzz-server
> + * Run:   ./fuzz-server (after passt.fuzz is listening)
> + *
> + * Copyright Red Hat
> + * Author: Anshu Kumari <anskuma@redhat.com>
> + */
> +
> +#include <linux/if_ether.h>
> +#include <netinet/in.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stdint.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <sys/mman.h>
> +#include <sys/socket.h>
> +#include <sys/un.h>
> +#include <sys/epoll.h>
> +#include <sys/shm.h>
> +#include <arpa/inet.h>
> +#include <net/ethernet.h>
> +#include <net/if_arp.h>
> +#include <netinet/ip.h>
> +#include <netinet/tcp.h>
> +
> +#define FUZZ_SOCK_PATH		"/tmp/passt_fuzz_1.socket"
> +#define FUZZ_TURN_PATH		"/dev/shm/passt_fuzz_turn"
> +#define FUZZ_XOR_OFF		(12 + 64 * 1024)
> +#define MAX_FRAME		1600
> +#define TCP_LISTEN_PORT		9999
> +#define TCP_MAX_EVENTS		16
> +
> +/**
> + * struct fuzz_turn - Turn-based synchronization flag (mmap'd shared memory)
> + * @turn:	0 = passt's turn, 1 = server's turn
> + */
> +struct fuzz_turn {
> +	uint32_t turn;
> +};
> +
> +/**
> + * swap_eth_ip() - Swap Ethernet MACs and IPv4 addresses
> + * @pkt:	Pointer to start of Ethernet frame
> + */
> +static void swap_eth_ip(uint8_t *pkt)
> +{
> +	struct ethhdr *eth = (void *)pkt;
> +	struct iphdr *iph = (void *)(eth + 1);
> +	uint8_t tmp_mac[ETH_ALEN];
> +	uint8_t tmp_ip[4];
> +
> +	memcpy(tmp_mac, eth->h_source, ETH_ALEN);
> +	memcpy(eth->h_source, eth->h_dest, ETH_ALEN);
> +	memcpy(eth->h_dest, tmp_mac, ETH_ALEN);
> +
> +	memcpy(tmp_ip, &iph->saddr, 4);
> +	memcpy(&iph->saddr, &iph->daddr, 4);
> +	memcpy(&iph->daddr, tmp_ip, 4);
> +}
> +
> +/**
> + * respond_arp() - Generate ARP reply for an ARP request
> + * @in:		Incoming frame
> + * @in_len:	Incoming frame length
> + * @out:	Output buffer for response
> + *
> + * Swaps sender hardware and protocol addresses to form a
> + * valid ARP reply. Only responds to ARP requests (op=1).
> + *
> + * Return: response length, or 0 if not an ARP request
> + */
> +static int respond_arp(const uint8_t *in, uint32_t in_len, uint8_t *out)
> +{
> +	/* Min ARP Frame size: eth(14) + ARP Header(8) + Payload(20) */
> +	if (in_len < 42)
> +		return 0;
> +
> +	const struct ethhdr *eth = (const void *)in;
> +	const struct arphdr *arp = (const void *)(eth + 1);
> +
> +	if (eth->h_proto != htons(ETH_P_ARP) ||
> +	    arp->ar_op != htons(ARPOP_REQUEST))
> +		return 0;
> +
> +	memcpy(out, in, in_len);
> +
> +	struct ethhdr *out_eth = (void *)out;
> +	struct arphdr *out_arp = (void *)(out_eth + 1);
> +
> +	/* set MAC addresses for output buffer */
> +	memcpy(out_eth->h_dest, eth->h_source, ETH_ALEN);
> +	memcpy(out_eth->h_source, eth->h_dest, ETH_ALEN);
> +
> +	/* set ARP operation to REPLY */
> +	out_arp->ar_op = htons(ARPOP_REPLY);
> +
> +	/* Swap ARP Sender and Target fields */
> +	uint8_t *sender = (uint8_t *)(out_arp + 1);
> +	uint8_t *target = sender + 10;
> +	uint8_t tmp[10];
> +
> +	memcpy(tmp, sender, 10);
> +	memcpy(sender, target, 10);
> +	memcpy(target, tmp, 10);
> +
> +	return in_len;
> +}
> +
> +/**
> + * respond_tcp_syn() - Generate SYN-ACK for a TCP SYN
> + * @in:         Incoming frame
> + * @in_len:     Incoming frame length
> + * @out:        Output buffer for response
> + *
> + * Swaps MAC/IP/port addresses and crafts a stateless SYN-ACK with
> + * a fixed ISN of 1000. Only responds to pure SYN (no ACK set).
> + *
> + * Return: response length, or 0 if not a TCP SYN
> + */
> +static int respond_tcp_syn(const uint8_t *in, uint32_t in_len, uint8_t *out)
> +{
> +	const struct ethhdr *eth = (const void *)in;
> +	const struct iphdr *iph;
> +	const struct tcphdr *th;
> +	struct tcphdr *out_th;
> +	uint32_t ihl, hdr_len;
> +
> +	/* Combined minimum length check for Eth + IP + TCP */
> +	if (in_len < sizeof(*eth) + sizeof(*iph) + sizeof(*th))
> +		return 0;
> +
> +	if (eth->h_proto != htons(ETH_P_IP))
> +		return 0;
> +
> +	iph = (const void *)(eth + 1);
> +	ihl = iph->ihl * 4;
> +
> +	if (iph->protocol != IPPROTO_TCP ||
> +	    in_len < sizeof(*eth) + ihl + sizeof(*th))
> +		return 0;
> +
> +	th = (const void *)((const uint8_t *)iph + ihl);
> +
> +	/* Only respond to pure SYN packets (no ACK set) */
> +	if (!th->syn || th->ack)
> +		return 0;
> +
> +	hdr_len = sizeof(*eth) + ihl + sizeof(*th);
> +	memcpy(out, in, hdr_len);
> +	swap_eth_ip(out);
> +
> +	/* Craft SYN-ACK response header */
> +	out_th = (struct tcphdr *)(out + sizeof(*eth) + ihl);
> +	out_th->source  = th->dest;
> +	out_th->dest    = th->source;
> +	out_th->seq     = htonl(1000);
> +	out_th->ack_seq = htonl(ntohl(th->seq) + 1);
> +	out_th->syn     = 1;
> +	out_th->ack     = 1;
> +	out_th->doff    = 5;
> +	out_th->check   = 0;
> +
> +	return hdr_len;
> +}
> +
> +/**
> + * generate_response() - Try all protocol responders on a frame
> + * @in:		Incoming frame
> + * @in_len:	Incoming frame length
> + * @out:	Output buffer for response
> + *
> + * Tries ARP then TCP SYN responders in order. Returns the first
> + * successful response.
> + *
> + * Return: response length, or 0 if no responder matched
> + */
> +static int generate_response(const uint8_t *in, uint32_t in_len, uint8_t *out)
> +{
> +	int len;
> +
> +	if (in_len < sizeof(struct ethhdr))
> +		return 0;
> +
> +	if ((len = respond_arp(in, in_len, out)) > 0)
> +		return len;
> +	if ((len = respond_tcp_syn(in, in_len, out)) > 0)
> +		return len;
> +
> +	return 0;
> +}
> +
> +/**
> + * map_afl_shm() - Map AFL++ shared memory for XOR mutation
> + *
> + * Reads __AFL_SHM_FUZZ_ID from the AFL++ environment
> + * variable and attaches the corresponding shared memory
> + * segment read-only.
> + *
> + * Return: pointer to mapped SHM, or NULL if not available
> + */
> +static uint8_t *map_afl_shm(void)
> +{
> +	const char *id_str = getenv("__AFL_SHM_FUZZ_ID");
> +	int shm_id;
> +	uint8_t *map;
> +
> +	if (!id_str)
> +		return NULL;
> +
> +	shm_id = atoi(id_str);
> +	map = shmat(shm_id, NULL, SHM_RDONLY);
> +	if (map == (void *)-1)
> +		return NULL;
> +
> +	return map;
> +}
> +
> +/**
> + * apply_xor_mask() - XOR buffer with AFL++ shared memory data
> + * @buf:	Buffer to mutate in place
> + * @len:	Buffer length
> + * @afl_buf:	AFL++ shared memory
> + *
> + * XORs each byte of @buf with the corresponding byte from the
> + * AFL++ buffer starting at offset FUZZ_XOR_OFF (65548). This
> + * lets AFL++ mutate server responses.
> + */
> +static void apply_xor_mask(uint8_t *buf, int len, const uint8_t *afl_buf)
> +{
> +	int i;
> +
> +	if (!afl_buf)
> +		return;
> +
> +	for (i = 0; i < len; i++)
> +		buf[i] ^= afl_buf[FUZZ_XOR_OFF + i];
> +}
> +
> +/* ---- TCP Host Listener (Loopback) ---- */
> +
> +static int tcp_listen_fd = -1;
> +static int tcp_epfd = -1;
> +
> +/**
> + * tcp_listener_init() - Set up TCP listener on loopback port 9999
> + *
> + * Creates an epoll instance and a non-blocking TCP listener on
> + * 127.0.0.1:9999 with SO_REUSEADDR.
> + *
> + * Return: 0 on success, -1 on failure
> + */
> +static int tcp_listener_init(void)
> +{
> +	struct sockaddr_in sa = {
> +		.sin_family = AF_INET,
> +		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
> +		.sin_port = htons(TCP_LISTEN_PORT),
> +	};
> +	struct epoll_event ev;
> +	int opt = 1;
> +
> +	tcp_epfd = epoll_create1(EPOLL_CLOEXEC);
> +	tcp_listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
> +	if (tcp_epfd < 0 || tcp_listen_fd < 0)
> +		return -1;
> +
> +	setsockopt(tcp_listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
> +
> +	if (bind(tcp_listen_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0 ||
> +	    listen(tcp_listen_fd, 128) < 0)
> +		return -1;
> +
> +	ev.events = EPOLLIN;
> +	ev.data.fd = tcp_listen_fd;
> +	epoll_ctl(tcp_epfd, EPOLL_CTL_ADD, tcp_listen_fd, &ev);
> +
> +	return 0;
> +}
> +
> +/**
> + * tcp_handle_accept() - Drain all pending TCP connections
> + *
> + * Sets SO_LINGER with l_linger=0 on every accepted connection,
> + * which does the immediate RST on connection close instead of
> + * waiting for connection to close on it's own.
> + */
> +static void tcp_handle_accept(void)
> +{
> +	struct linger lg = { .l_onoff = 1, .l_linger = 0 };
> +	struct epoll_event ev;
> +	int fd;
> +
> +	/* Drain ALL pending connections in the backlog */
> +	while ((fd = accept4(tcp_listen_fd, NULL, NULL,
> +			     SOCK_NONBLOCK)) >= 0) {
> +		setsockopt(fd, SOL_SOCKET, SO_LINGER, &lg, sizeof(lg));
> +
> +		ev.events = EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR;
> +		ev.data.fd = fd;
> +		epoll_ctl(tcp_epfd, EPOLL_CTL_ADD, fd, &ev);
> +	}
> +}
> +
> +/**
> + * tcp_handle_data() - Read TCP data, XOR-mutate, echo back
> + * @fd:		Connected TCP socket
> + * @afl_buf:	AFL++ shared memory for XOR mask
> + */
> +static void tcp_handle_data(int fd, const uint8_t *afl_buf)
> +{
> +	uint8_t buf[MAX_FRAME];
> +	ssize_t n, written, ret;
> +
> +	n = read(fd, buf, sizeof(buf));
> +	if (n <= 0) {
> +		epoll_ctl(tcp_epfd, EPOLL_CTL_DEL, fd, NULL);
> +		close(fd);
> +		return;
> +	}
> +
> +	apply_xor_mask(buf, (uint32_t)n, afl_buf);
> +
> +	/* Ensure all n bytes are written, handling short writes */
> +	written = 0;
> +	while (written < n) {
> +		ret = write(fd, buf + written, (size_t)(n - written));
> +		if (ret <= 0) {
> +			if (ret < 0 && (errno == EAGAIN ||
> +					errno == EWOULDBLOCK))
> +				continue;
> +
> +			epoll_ctl(tcp_epfd, EPOLL_CTL_DEL, fd, NULL);
> +			close(fd);
> +			return;
> +		}
> +		written += ret;
> +	}
> +}
> +
> +/**
> + * tcp_process_events() - Non-blocking poll for TCP events
> + * @afl_buf:	AFL++ shared memory
> + */
> +static void tcp_process_events(const uint8_t *afl_buf)
> +{
> +	struct epoll_event events[TCP_MAX_EVENTS];
> +	int nfds, i;
> +
> +	nfds = epoll_wait(tcp_epfd, events, TCP_MAX_EVENTS, 0);
> +
> +	for (i = 0; i < nfds; i++) {
> +		int fd = events[i].data.fd;
> +
> +		if (fd == tcp_listen_fd) {
> +			tcp_handle_accept();
> +		} else if (events[i].events & (EPOLLIN | EPOLLRDHUP)) {
> +			tcp_handle_data(fd, afl_buf);
> +		} else if (events[i].events & (EPOLLHUP | EPOLLERR)) {
> +			epoll_ctl(tcp_epfd, EPOLL_CTL_DEL, fd, NULL);
> +			close(fd);
> +		}
> +	}
> +}
> +
> +/**
> + * create_turn_flag() - Create and mmap the turn synchronization flag
> + *
> + * Creates FUZZ_TURN_PATH in /dev/shm and maps it
> + * as shared memory. Both passt and fuzz-server map this file to
> + * coordinate turn-based frame exchange via atomic load/store.
> + *
> + * Return: pointer to mapped turn struct, or NULL on failure
> + */
> +static struct fuzz_turn *create_turn_flag(void)
> +{
> +	struct fuzz_turn *t;
> +	int fd;
> +
> +	fd = open(FUZZ_TURN_PATH, O_RDWR | O_CREAT | O_TRUNC, 0644);
> +	if (fd < 0)
> +		return NULL;
> +
> +	if (ftruncate(fd, sizeof(struct fuzz_turn)) < 0) {
> +		close(fd);
> +		return NULL;
> +	}
> +
> +	t = mmap(NULL, sizeof(*t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
> +	close(fd);
> +
> +	if (t == MAP_FAILED)
> +		return NULL;
> +
> +	t->turn = 0;
> +	return t;
> +}
> +
> +/**
> + * main() - Server entry point
> + *
> + * Outer loop reconnects to passt's UNIX socket on each AFL++ fork
> + * server restart. Inner loop handles turn-based TAP frame exchange
> + * and TCP events concurrently.
> + *
> + * Return: 0 on success, 1 on initialization failure
> + */
> +int main(void)
> +{
> +	struct sockaddr_un addr = { .sun_family = AF_UNIX };
> +	uint8_t frame[MAX_FRAME], response[MAX_FRAME];
> +	struct fuzz_turn *turn;
> +	uint8_t *afl_buf;
> +
> +	strncpy(addr.sun_path, FUZZ_SOCK_PATH, sizeof(addr.sun_path) - 1);
> +
> +	turn = create_turn_flag();
> +	if (!turn || tcp_listener_init() < 0) {
> +		(void)fprintf(stderr,
> +			      "fuzz-server: tcp initialization failed\n");
> +		return 1;
> +	}
> +
> +	afl_buf = map_afl_shm();
> +	(void)fprintf(stderr,
> +		      "fuzz-server: ready, TCP listening on 127.0.0.1:%d\n",
> +		TCP_LISTEN_PORT);
> +
> +	/*
> +	 * Reconnect UNIX socket on passt restart — AFL++'s fork server
> +	 * restarts passt on each iteration.
> +	 */
> +	while (1) {
> +		ssize_t n;
> +		int sock;
> +
> +		sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
> +		if (sock < 0)
> +			return 1;
> +
> +		while (connect(sock, (struct sockaddr *)&addr,
> +			       sizeof(addr)) < 0)
> +			usleep(10000);
> +
> +		while (1) {
> +			tcp_process_events(afl_buf);
> +
> +			if (__atomic_load_n(&turn->turn,
> +					    __ATOMIC_ACQUIRE) == 1) {
> +				n = recv(sock, frame, MAX_FRAME,
> +					 MSG_DONTWAIT);
> +				if (n > 0) {
> +					int resp_len;
> +
> +					resp_len = generate_response(
> +							frame, n, response);
> +					if (resp_len > 0) {
> +						apply_xor_mask(response,
> +							       resp_len,
> +							       afl_buf);
> +						send(sock, response,
> +						     resp_len, MSG_NOSIGNAL);
> +					}
> +				} else if (n == 0 ||
> +					   (n < 0 && errno != EAGAIN &&
> +					    errno != EWOULDBLOCK)) {
> +					__atomic_store_n(&turn->turn, 0,
> +							 __ATOMIC_RELEASE);
> +					break;
> +				}
> +
> +				__atomic_store_n(&turn->turn, 0,
> +						 __ATOMIC_RELEASE);
> +			}
> +		}
> +
> +		close(sock);
> +	}
> +
> +	return 0;
> +}
> diff --git a/fuzzing/README.fuzzing.md b/fuzzing/README.fuzzing.md
> new file mode 100644
> index 0000000..f23e265
> --- /dev/null
> +++ b/fuzzing/README.fuzzing.md
> @@ -0,0 +1,95 @@
> +## Fuzzing passt with AFL++
> +
> +### Prerequisites
> +
> +- AFL++ (afl-clang-fast, afl-fuzz)
> +
> +### Build
> +
> +```
> +make fuzz
> +cp passt passt.fuzz
> +make fuzz-server
> +```
> +
> +This produces:
> +- `passt.fuzz` -- instrumented with AFL++ and AddressSanitizer
> +- `fuzz-server` -- test server for bidirectional protocol fuzzing
> +
> +To use a specific AFL++ installation:
> +
> +```
> +make FUZZ_CC=/path/to/afl-clang-fast fuzz
> +```
> +
> +### Run
> +
> +Start the test server first, then the fuzzer:
> +
> +```
> +# Terminal 1 -- test server:
> +./fuzz-server
> +
> +# Terminal 2 -- fuzzer:
> +afl-fuzz -i fuzzing/testcase_dir -o fuzzing/sync_dir \
> +  -- ./passt.fuzz --foreground
> +```
> +
> +Multi-core (secondary instances share the corpus):
> +
> +```
> +# Terminal 1 -- main instance:
> +afl-fuzz -M main -i fuzzing/testcase_dir -o fuzzing/sync_dir \
> +  -- ./passt.fuzz --foreground
> +
> +# Terminal 2 -- secondary with different power schedule:
> +afl-fuzz -S variant1 -p rare -i fuzzing/testcase_dir \
> +  -o fuzzing/sync_dir -- ./passt.fuzz --foreground
> +```
> +
> +### Architecture
> +
> +The fuzzer uses AFL++ persistent mode with shared memory fuzzing.
> +Each iteration:
> +
> +1. Resets deterministic state (clock, flow table, epoll)
> +2. Reads an epoll event and packet data from AFL++ shared memory
> +3. For TAP events: injects a packet with fixed L2/L3/L4 headers
> +   into the tap pipeline via tap_add_packet() + tap_handler().
> +   The TCP destination port is fixed to 9999 (the test server's
> +   listening port).
> +4. Exchanges a turn flag with fuzz-server for bidirectional flow.
> +5. Calls passt_worker() to process the epoll event
> +6. Polls for host-side TCP events (connect completion, server data)
> +
> +The test server connects to passt's UNIX socket (SOCK_SEQPACKET)
> +and listens on 127.0.0.1:9999 for TCP connections. It responds to
> +ARP requests and TCP SYNs on the UNIX socket, and echoes TCP data
> +(XOR'd with AFL++ shared memory) on the loopback side.
> +
> +Deterministic wrappers in fuzz.c replace clock_gettime(),
> +getrandom(), getsockopt(TCP_INFO), and recv/recvmsg/recvfrom/
> +recvmmsg to eliminate non-determinism from kernel state. The recv
> +wrappers return AFL++ buffer data for non-TAP file descriptors,
> +giving the fuzzer control over what passt "receives" from host
> +sockets.
> +
> +### Seed inputs
> +
> +`testcase_dir/empty.bin` is a 12-byte zero file
> +(sizeof(struct epoll_event)). AFL++ discovers packet formats
> +through mutation from this minimal seed.
> +
> +### Reproducing crashes
> +
> +```
> +./passt.fuzz --foreground < \
> +  fuzzing/sync_dir/default/crashes/id:000000,...
> +```
> +
> +Minimize a crash input:
> +
> +```
> +afl-tmin -i fuzzing/sync_dir/default/crashes/id:000000,... \
> +  -o crash_minimized -- ./passt.fuzz --foreground
> +```
> diff --git a/fuzzing/testcase_dir/empty.bin b/fuzzing/testcase_dir/empty.bin
> new file mode 100644
> index 0000000000000000000000000000000000000000..ce58bc9f84b9623e708de4eb8427a57d9f9a160f
> GIT binary patch
> literal 12
> KcmZQzKmY&$3;+QD
> 
> literal 0
> HcmV?d00001
> 
> -- 
> 2.55.0
> 

-- 
David Gibson (he or they)	| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you, not the other way
				| around.
http://www.ozlabs.org/~dgibson

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

      parent reply	other threads:[~2026-08-14  5:50 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-12  7:26 [PATCH 0/5] Add AFL++ fuzzing support for passt Anshu Kumari
2026-08-12  7:26 ` [PATCH 1/5] fuzz: Add deterministic wrappers for system calls Anshu Kumari
2026-08-13  3:46   ` David Gibson
2026-08-12  7:26 ` [PATCH 2/5] fuzz: Add flow type guards for fuzzing stability Anshu Kumari
2026-08-13  4:45   ` David Gibson
2026-08-12  7:26 ` [PATCH 3/5] fuzz: Bypass isolation and adapt sockets for AFL++ Anshu Kumari
2026-08-13  5:04   ` David Gibson
2026-08-12  7:26 ` [PATCH 4/5] fuzz: Add AFL++ persistent mode fuzz loop Anshu Kumari
2026-08-13  6:34   ` Stefano Brivio
2026-08-14  1:55   ` David Gibson
2026-08-12  7:26 ` [PATCH 5/5] fuzz: Add test server for bidirectional protocol fuzzing Anshu Kumari
2026-08-13  7:53   ` Stefano Brivio
2026-08-14  5:40     ` David Gibson
2026-08-14  5:50   ` David Gibson [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=an6slIfmlBj0mRns@zatzit \
    --to=david@gibson.dropbear.id.au \
    --cc=abdobngad@gmail.com \
    --cc=aerosound161@gmail.com \
    --cc=anskuma@redhat.com \
    --cc=lvivier@redhat.com \
    --cc=passt-dev@passt.top \
    --cc=sbrivio@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).