From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: passt.top; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=B/U9nTYQ; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by passt.top (Postfix) with ESMTPS id 9DB445A0272 for ; Thu, 13 Aug 2026 08:34:14 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1786602853; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=DwshkEwUyeacTpONAB9dffeScIKOb6H06A1DrJ3mbaU=; b=B/U9nTYQjhFOB0YVjpDezrEaN/jh398L0BJYsz8iTpxDb5j/arX4UXRgeWkv23tKl+2QiH IJYivC5scv5192ZjDSLhZaXTcVvc0K/MABEuYfP170L258ykSejSVSSlkg/4OIzXDSAOMy vLFNYSCxpSXDc39lsIz3ZrPoi5vDUdE= Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-201-ZaiDatLHP4aofPmdDYy6xA-1; Thu, 13 Aug 2026 02:34:11 -0400 X-MC-Unique: ZaiDatLHP4aofPmdDYy6xA-1 X-Mimecast-MFC-AGG-ID: ZaiDatLHP4aofPmdDYy6xA_1786602851 Received: by mail-wm1-f71.google.com with SMTP id 5b1f17b1804b1-4996832e7b4so3873295e9.1 for ; Wed, 12 Aug 2026 23:34:11 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786602851; x=1787207651; h=date:content-transfer-encoding:content-type:mime-version :organization:references:in-reply-to:message-id:subject:cc:to:from :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to:content-type; bh=DwshkEwUyeacTpONAB9dffeScIKOb6H06A1DrJ3mbaU=; b=dfUhkSRJuZ76mP+1knHvmJQvxHQfEodDrm5i/A/N+5GISefGalLY6lBIGhWcYIXjl9 Gs5V68RZl1VYExMQLv5qfwa35cKelvbd0PULjlYHoZgV0Oy3PYz3hdw6l/tCsagRwALU H4aymXmHPUhkRtEl9aG5kGzxkq82PswqbJFdiuMKJczpyWGv+3U256nwzRUdirqkNUsn XjygUKFqT5Kod2/ADqrPpHBmOqxvic5ZqqfYzlMgXVzAedSUuZ4NIFz2Z7/83Ji+sEzU OL1BoraV94NHAV7d/+oWP78yO22p2Sa/fLupGGM2FdpkuwJddCJx3ty8eBbzt2AfXU4x r9ug== X-Forwarded-Encrypted: i=1; AHgh+RpctfEfS/elafD4LtWrEyxup6JtaibPQeJLUeh50NPPF5z/Kg2Cb1hHAhBhWYhj9i54w7rJ9Salo7A=@passt.top X-Gm-Message-State: AOJu0Yz6KsqojzOps5hoSW3XukUni6nugj304KEUwUAEhV7+eeAnil7i 92XJdPpSxXzHD0HvJQkGJT/vu3EtQ3V10DkUD0DegbNj4JEMAusgw/Y53F2GbIZyZ+WAalYZV5X XWJOz2jHXv0wq3A7hx4y082MoDHKweV4NwPxDyPJolMtj/1HJBDq8Sw== X-Gm-Gg: AR+sD10asYAk0OphydW0kZOa+pmWh5DEvstdm0OabiZ4slxJAWWFPWgbUMUt2sTxehK 0m5/7Fs4jNTeOjDFviG2dnTMuPBI6twakg0cdKNKwhWW32yxOrlHyuwhv8bUM0MLWnSD2qIdbfY gJnj7jr2Ve+MD/nROsOcvAwjqa+CQtCAymPwk/OnvdpZlKkZ/pEdVY+oAaWqhEz+SpQ+3QcQ+VB H7COKeJc+XyCbY5WW69xbpO07KQ8L0FWgl2tjzWMcW80EdvD3GytshqGKM9rBZjQAyhtJWXHaW0 IO/IvY0RTHU/O51DZapLwQecnklSuqgIHj30qehsbPT0G+ESqy7Gmbu7RLitHmSsMoSm7Xtpl06 vvIMhSkC1wcY= X-Received: by 2002:a05:600c:4584:b0:495:5d6d:9cc1 with SMTP id 5b1f17b1804b1-499821baebamr28497415e9.0.1786602850498; Wed, 12 Aug 2026 23:34:10 -0700 (PDT) X-Received: by 2002:a05:600c:4584:b0:495:5d6d:9cc1 with SMTP id 5b1f17b1804b1-499821baebamr28496785e9.0.1786602849945; Wed, 12 Aug 2026 23:34:09 -0700 (PDT) Received: from maya.myfinge.rs (ifcgrfdd.trafficplex.cloud. [2a10:fc81:a806:d6a9::1]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-49982130365sm38396955e9.7.2026.08.12.23.34.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 12 Aug 2026 23:34:09 -0700 (PDT) From: Stefano Brivio To: Anshu Kumari Subject: Re: [PATCH 4/5] fuzz: Add AFL++ persistent mode fuzz loop Message-ID: <20260813083352.2cbf2af3@elisabeth> In-Reply-To: <20260812072630.3235261-5-anskuma@redhat.com> References: <20260812072630.3235261-1-anskuma@redhat.com> <20260812072630.3235261-5-anskuma@redhat.com> Organization: Red Hat X-Mailer: Claws Mail 4.2.0 (GTK 3.24.49; x86_64-pc-linux-gnu) MIME-Version: 1.0 Date: Thu, 13 Aug 2026 08:34:08 +0200 (CEST) X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: kedRZ-QkxJAM2hUwvOl_dZLvdfdvqnozZy2f7h-hUCc_1786602851 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Message-ID-Hash: 2FWT7RGXH2R5CLLCMJS4VV7VPRXSGAUQ X-Message-ID-Hash: 2FWT7RGXH2R5CLLCMJS4VV7VPRXSGAUQ X-MailFrom: sbrivio@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: david@gibson.dropbear.id.au, passt-dev@passt.top, aerosound161@gmail.com, abdobngad@gmail.com, lvivier@redhat.com X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Not a complete review, just a few notes, mostly about the general concept: On Wed, 12 Aug 2026 12:56:27 +0530 Anshu Kumari wrote: > 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. This complexity could probably be avoided if you switch to a model where the test server is just operating on the host side of things (accepting TCP connections and replying). More on that in a bit as a comment to 5/5. > - 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 > --- > 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 > #include > #include > +#include > > #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)) { I think this loop is a useful implementation, as far as I understand the purpose is to avoid that AFL++ needs to restarts us at every new attempt. So I think it makes sense that you reset the state below. But, inside this loop, we need to allow AFL++ to send us arbitrary sequences of packets, not just inject a single one. Not much will happen with a single packet. > + 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); This is needed to let AFL++ generate events. But if you call epoll_wait() below, with 'events' (which is not set from 'ev'), we won't actually use those events generated by AFL++. I guess you're only getting events from the test server. But I don't think that hardcoding a sequence of: - single packet from AFL++ (tap side) - four packets from the test server (all host side I guess?) will actually result in any meaningful exchange (including a TCP connection). By the way, in the approach I was suggesting, where AFL++ would act as guest and feeding data to us directly, while the test server would act as host / internet side (with data fed from AFL++), AFL++ would only generate tap-side events, so we would probably need to *add* those to 'ev' while also reacting to host-side events (for example the test server accepting a connection, or sending data over an accepted connection). > + > + /* 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 */ I guess this whole path is needed to quickly get something working, but, eventually, we shouldn't need this. We need to give the possibility to AFL++ to give us multiple packets, and possibly (or especially) malformed ones. If it just generates payload, that looks relatively "safe" and is relatively unlikely to discover issues. > + 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 */ > } -- Stefano