public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
From: Anshu Kumari <anskuma@redhat.com>
To: david@gibson.dropbear.id.au, sbrivio@redhat.com, passt-dev@passt.top
Cc: aerosound161@gmail.com, abdobngad@gmail.com, anskuma@redhat.com,
	lvivier@redhat.com
Subject: [PATCH 4/5] fuzz: Add AFL++ persistent mode fuzz loop
Date: Wed, 12 Aug 2026 12:56:27 +0530	[thread overview]
Message-ID: <20260812072630.3235261-5-anskuma@redhat.com> (raw)
In-Reply-To: <20260812072630.3235261-1-anskuma@redhat.com>

Add the AFL++ persistent mode fuzz loop to passt.c main().
The loop uses __AFL_LOOP() for in-process iteration and
__AFL_FUZZ_TESTCASE_BUF for shared memory fuzzing.

Each iteration:
  - Resets deterministic clock, flow table, and epoll instance.
  - Drains stale data from the TAP socket.
  - Reads an epoll event from the AFL++ buffer.
  - For TAP events: constructs a packet with fixed L2/L3/L4
    headers and injects it via tap_add_packet() + tap_handler().
  - Exchanges a turn flag with the test server for
    bidirectional flow over the UNIX socket.
  - Calls passt_worker() to process the event.
  - Polls for host-side TCP events via epoll_wait().
  - Runs post_handler() for deferred work.

Added the 'make fuzz' target which builds passt with
afl-clang-fast, -DFUZZING, -DNDEBUG, and AddressSanitizer.

Signed-off-by: Anshu Kumari <anskuma@redhat.com>
---
 Makefile |   8 +++
 passt.c  | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 197 insertions(+)

diff --git a/Makefile b/Makefile
index fe1df58..8e4121e 100644
--- a/Makefile
+++ b/Makefile
@@ -123,6 +123,14 @@ valgrind: BASE_CPPFLAGS += -DVALGRIND
 valgrind: BASE_CFLAGS += -g
 valgrind: all
 
+FUZZ_CC ?= afl-clang-fast
+
+.PHONY: fuzz
+
+fuzz:
+	$(MAKE) clean
+	$(MAKE) CC="$(FUZZ_CC)" CPPFLAGS="-DFUZZING -DNDEBUG" CFLAGS="-g -fsanitize=address" passt
+
 .PHONY: clean
 clean:
 	$(RM) $(BIN) *~ *.o seccomp.h seccomp_repair.h seccomp_pesto.h pasta.1 \
diff --git a/passt.c b/passt.c
index 5054551..e026eb2 100644
--- a/passt.c
+++ b/passt.c
@@ -35,6 +35,7 @@
 #include <sys/prctl.h>
 #include <netinet/if_ether.h>
 #include <libgen.h>
+#include <netinet/tcp.h>
 
 #include "util.h"
 #include "passt.h"
@@ -54,12 +55,56 @@
 #include "repair.h"
 #include "netlink.h"
 #include "epoll_ctl.h"
+#include "flow_table.h"
+#include "fuzz.h"
 
 #define NUM_EPOLL_EVENTS	8
 
 #define TIMER_INTERVAL_		MIN(TCP_TIMER_INTERVAL, FWD_PORT_SCAN_INTERVAL)
 #define TIMER_INTERVAL		MIN(TIMER_INTERVAL_, FLOW_TIMER_INTERVAL)
 
+#ifdef FUZZING
+
+/* AFL++ persistent mode / shared memory fuzzing compatibility macros. */
+#ifndef __AFL_FUZZ_TESTCASE_LEN
+  ssize_t		fuzz_len;
+  unsigned char		fuzz_buf[1024 * 1024];
+# define __AFL_FUZZ_TESTCASE_LEN	fuzz_len
+# define __AFL_FUZZ_TESTCASE_BUF	fuzz_buf
+# define __AFL_FUZZ_INIT()		void sync(void)
+# define __AFL_LOOP(x)		\
+	((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0)
+# define __AFL_INIT()		sync()
+#endif
+
+#ifdef __AFL_HAVE_MANUAL_CONTROL
+  __AFL_FUZZ_INIT();
+#endif
+
+static struct fuzz_turn *fuzz_turn_ptr;
+
+/**
+ * fuzz_turn_connect() - Map the turn flag shared memory
+ *
+ * Return: pointer to mapped turn flag, or NULL on failure
+ */
+static struct fuzz_turn *fuzz_turn_connect(void)
+{
+	struct fuzz_turn *t;
+	int fd;
+
+	fd = open(FUZZ_TURN_PATH, O_RDWR);
+	if (fd < 0)
+		return NULL;
+
+	t = mmap(NULL, sizeof(*t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+	close(fd);
+
+	return (t == MAP_FAILED) ? NULL : t;
+}
+
+#endif
+
 char pkt_buf[PKT_BUF_BYTES]	__attribute__ ((aligned(PAGE_SIZE)));
 
 struct ctx passt_ctx = {
@@ -282,9 +327,17 @@ static void passt_worker(void *opaque, int nfds, struct epoll_event *events)
 			icmp_sock_handler(c, ref, &now);
 			break;
 		case EPOLL_TYPE_VHOST_CMD:
+#ifdef FUZZING
+			if (!c->vdev)
+				break;
+#endif
 			vu_control_handler(c->vdev, c->fd_tap, eventmask);
 			break;
 		case EPOLL_TYPE_VHOST_KICK:
+#ifdef FUZZING
+			if (!c->vdev)
+				break;
+#endif
 			vu_kick_cb(c->vdev, ref, &now);
 			break;
 		case EPOLL_TYPE_REPAIR_LISTEN:
@@ -450,6 +503,141 @@ int main(int argc, char **argv)
 
 	timer_init(c, &now);
 
+#ifdef FUZZING
+	fuzz_turn_ptr = fuzz_turn_connect();
+
+#define FUZZ_LOOP_ITERATIONS	10000
+#define FUZZ_DRAIN_BUF_SIZE	1600
+
+#ifdef __AFL_HAVE_MANUAL_CONTROL
+	__AFL_INIT();
+#endif
+	{
+		unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
+
+		while (__AFL_LOOP(FUZZ_LOOP_ITERATIONS)) {
+			int len = __AFL_FUZZ_TESTCASE_LEN;
+			int injected = 0;
+			int pkt_len, round;
+			struct epoll_event ev;
+			union epoll_ref ref;
+			int min_pkt = sizeof(struct ethhdr) +
+				      sizeof(struct iphdr) +
+				      sizeof(struct tcphdr);
+
+			if (len < (int)sizeof(ev))
+				continue;
+
+			/* Reset clock, flow table and epoll for each
+			 * AFL++ iteration.
+			 */
+			fuzz_clock_reset();
+			clock_gettime(CLOCK_MONOTONIC, &now);
+			timer_init(c, &now);
+
+			flow_init();
+
+			/* Recreate epoll instance */
+			close(c->epollfd);
+			c->epollfd = epoll_create1(EPOLL_CLOEXEC);
+			flow_epollid_register(EPOLLFD_ID_DEFAULT, c->epollfd);
+
+			if (c->fd_tap >= 0) {
+				union epoll_ref tref = {
+					.type = EPOLL_TYPE_TAP_PASST,
+					.fd = c->fd_tap
+				};
+				epoll_add(c->epollfd,
+					  EPOLLIN | EPOLLRDHUP, tref);
+
+				/* Drain stale socket data */
+				char drain[FUZZ_DRAIN_BUF_SIZE];
+				while (recv(c->fd_tap, drain, sizeof(drain),
+					    MSG_DONTWAIT) > 0);
+			}
+
+			/* Read epoll event from AFL++ buffer */
+			memcpy(&ev, buf, sizeof(ev));
+			ref = *((union epoll_ref *)&ev.data.u64);
+
+			/* Set recv payload in AFL++ shared memory */
+			fuzz_recv_data = buf + FUZZ_RECV_OFF;
+			fuzz_recv_data_len =
+				(len > FUZZ_RECV_OFF + FUZZ_RECV_MAX)
+					     ? FUZZ_RECV_MAX
+					     : ((len > FUZZ_RECV_OFF)
+						? len - FUZZ_RECV_OFF : 0);
+
+			/* Inject fuzz packet for TAP events */
+			if (ref.type == EPOLL_TYPE_TAP_PASST ||
+			    ref.type == EPOLL_TYPE_TAP_PASTA) {
+				struct iov_tail data;
+				struct ethhdr *eh;
+				struct iphdr *iph;
+				struct tcphdr *th;
+
+				tap_flush_pools();
+				memset(pkt_buf, 0, min_pkt);
+
+				pkt_len = len - (int)sizeof(ev);
+				if (pkt_len > 0)
+					memcpy(pkt_buf, buf + sizeof(ev),
+					       pkt_len);
+				if (pkt_len < min_pkt)
+					pkt_len = min_pkt;
+
+				/* construct ethernet header */
+				eh = (struct ethhdr *)pkt_buf;
+				memcpy(eh->h_dest, c->our_tap_mac, ETH_ALEN);
+				memcpy(eh->h_source, c->guest_mac, ETH_ALEN);
+				eh->h_proto = htons(ETH_P_IP);
+
+				/* construct IPv4 header */
+				iph = (struct iphdr *)(pkt_buf + sizeof(*eh));
+				iph->version = 4;
+				iph->ihl = 5;
+				iph->protocol = IPPROTO_TCP;
+				iph->saddr = c->ip4.addr.s_addr;
+				iph->daddr = c->ip4.guest_gw.s_addr;
+				iph->tot_len = htons(pkt_len - sizeof(*eh));
+
+				/* Fix TCP Header */
+				th = (struct tcphdr *)(pkt_buf + sizeof(*eh) +
+								 sizeof(*iph));
+				th->dest = htons(9999);
+				if (th->doff < 5)
+					th->doff = 5;
+
+				data = IOV_TAIL_FROM_BUF(pkt_buf, pkt_len, 0);
+				tap_add_packet(c, &data, &now);
+				tap_handler(c, &now);
+				injected = 1;
+			}
+
+			/* Turn exchange -- only if data was sent */
+			if (injected && fuzz_turn_ptr) {
+				__atomic_store_n(&fuzz_turn_ptr->turn, 1,
+						 __ATOMIC_RELEASE);
+				while (__atomic_load_n(&fuzz_turn_ptr->turn,
+						      __ATOMIC_ACQUIRE) != 0);
+			}
+
+			passt_worker(c, 1, &ev);
+
+			/* Process host-side TCP events */
+			for (round = 0; round < 4; round++) {
+				nfds = epoll_wait(c->epollfd, events,
+						  NUM_EPOLL_EVENTS, 0);
+				if (nfds <= 0)
+					break;
+				passt_worker(c, nfds, events);
+			}
+
+			post_handler(c, &now);
+		}
+	}
+	return 0;
+#else
 loop:
 	/* NOLINTBEGIN(bugprone-branch-clone): intervals can be the same */
 	/* cppcheck-suppress [duplicateValueTernary, unmatchedSuppression] */
@@ -461,4 +649,5 @@ loop:
 	passt_worker(c, nfds, events);
 
 	goto loop;
+#endif /* FUZZING */
 }
-- 
2.55.0


  parent reply	other threads:[~2026-08-12  7:27 UTC|newest]

Thread overview: 9+ 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 ` Anshu Kumari [this message]
2026-08-12  7:26 ` [PATCH 5/5] fuzz: Add test server for bidirectional protocol fuzzing Anshu Kumari

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=20260812072630.3235261-5-anskuma@redhat.com \
    --to=anskuma@redhat.com \
    --cc=abdobngad@gmail.com \
    --cc=aerosound161@gmail.com \
    --cc=david@gibson.dropbear.id.au \
    --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).