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
next prev 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).