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=afYd5oH2; dkim-atps=neutral Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by passt.top (Postfix) with ESMTPS id 1C0D15A0269 for ; Mon, 17 Aug 2026 11:13:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1786958036; 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: in-reply-to:in-reply-to:references:references; bh=gORxm3WzbN3rST6R//77dbcd+bktg6Mj7cL8hVRKK6c=; b=afYd5oH2FhpMoH4Z2okpjimON+wUFcq+hHu2EibXYr35fjbthN1JKlUw7Wl8owJ2vb/38S 6NJ7F9RVxWYzdvP/9osmYljom4RxKftKYRTDVLxQuE09hTV4We0kcGd8Qf0xOSmKQQK5Xt ZKytEhvg6bP1FiTIDljZAuQiPar4Fqo= Received: from mail-lf1-f72.google.com (mail-lf1-f72.google.com [209.85.167.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-390-2oh4aaxTOVGyjAW8SBFVig-1; Mon, 17 Aug 2026 05:13:54 -0400 X-MC-Unique: 2oh4aaxTOVGyjAW8SBFVig-1 X-Mimecast-MFC-AGG-ID: 2oh4aaxTOVGyjAW8SBFVig_1786958033 Received: by mail-lf1-f72.google.com with SMTP id 2adb3069b0e04-5b01965458eso1962117e87.0 for ; Mon, 17 Aug 2026 02:13:53 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786958032; x=1787562832; h=content-type:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to:content-type; bh=gORxm3WzbN3rST6R//77dbcd+bktg6Mj7cL8hVRKK6c=; b=D6s3TL4m3rhVjwua9tp+SqmpkoJTZ5qfKZPDErFVpthnwDHY/tOwO91GNYTdOWS13E f+gY8tp+pYeLzol5XWBNi7z6gSsdFb45VQQslkorQUE7c4DTNv2VuzVmtqq7woYhNErQ WOQMcqr4FGSQ2H/jFerWlKZOmaxVtIMRwstbBE57omwy7tgPsfEmSEwwKFG0z77ldyqz OGIEdNrfZp8A98gUzDlk+83dznNe3fi5JwNhyEhyxpv98jqMwaBUq1NMZMII4fQJdk5g GSmF7HmYE175oNs+B+X6e7YZB/tcD3P9Dm/c3lWi5bt1e/JkFzdb+JfOZ+Slf6H1gj3q 9jQA== X-Forwarded-Encrypted: i=1; AHgh+RopPpjyZv7f9jCJkRMfNlWhbyzo/HvEx5KY0Xs55/2rJHOGnG0Yslxx3+r+lW3+fgeifjaZsUTh24w=@passt.top X-Gm-Message-State: AOJu0YzeS0hixpXJe5V8dHjZni8bD3RBi3PpkXUWHVefE/n+SxsFATHY dM27Nq2NR/RqWIF1ztPUXyUBWJkVAOJ0j2+Ha3rK9zL3OoiaDxxNzcMgGZlgxDU6BaHafInFQ34 irKppURPu58imEkqV/g7Z6YzQzeOR2fMa6Rk6t/0edFoe33lN55BpCzlQyOUT4g3LQsC7DuyPbj mgBzPfY9SyFahrUrJ4U5xAg/49datO X-Gm-Gg: AR+sD11sSIFT5G128795671qA6NyDcTZJ1h9NU/tBEj7A7m7o6j2Kj9hjV+Qu5qao/r nkdTzJVbjowA7hSAHMlBFPGu6DGpSkJPwjRK5erVQxyZeC3OcKa48XYrc+WaZrrglfv3VU0wJeL Y2pwmS9zOcmfi+Izrgp/QRYixsTnqVlHmhoBNNpnfF0vTBciP1okpT+sjvYXs3Secjyi89mGZan ni0u4+yapgkgyznfg== X-Received: by 2002:a05:6512:66ce:b0:5b2:a22c:50e7 with SMTP id 2adb3069b0e04-5b45918ec33mr2391721e87.32.1786958032246; Mon, 17 Aug 2026 02:13:52 -0700 (PDT) X-Received: by 2002:a05:6512:66ce:b0:5b2:a22c:50e7 with SMTP id 2adb3069b0e04-5b45918ec33mr2391700e87.32.1786958031580; Mon, 17 Aug 2026 02:13:51 -0700 (PDT) MIME-Version: 1.0 References: <20260812072630.3235261-1-anskuma@redhat.com> <20260812072630.3235261-5-anskuma@redhat.com> In-Reply-To: From: Anshu Kumari Date: Mon, 17 Aug 2026 14:43:40 +0530 X-Gm-Features: AcwNN1VHmkqsYbtWQNq6UfI-NTIeVrj5SiBcF7YoyasWlu8HaVHTryuKRojcMgI Message-ID: Subject: Re: [PATCH 4/5] fuzz: Add AFL++ persistent mode fuzz loop To: David Gibson X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: opeJdg4uJzdxK5jmVmxdDdGzA6lTD2dZC5dX01YNadI_1786958033 X-Mimecast-Originator: redhat.com Content-Type: multipart/alternative; boundary="000000000000b37f8f06593a9682" Message-ID-Hash: O3X6ME6RVQXLYOVZ2X5N2FCTVAC4ML7B X-Message-ID-Hash: O3X6ME6RVQXLYOVZ2X5N2FCTVAC4ML7B X-MailFrom: anskuma@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: sbrivio@redhat.com, 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: --000000000000b37f8f06593a9682 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Mon, Aug 17, 2026 at 9:16=E2=80=AFAM David Gibson wrote: > On Fri, Aug 14, 2026 at 04:02:58PM +0530, Anshu Kumari wrote: > > On Fri, Aug 14, 2026 at 7:27=E2=80=AFAM David Gibson < > david@gibson.dropbear.id.au> > > wrote: > > > > > On Wed, Aug 12, 2026 at 12:56:27PM +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. > > > > - 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. > > > > > > Stefano's concerns generally seconded (although I haven't really got > > > my head around the role of the test server in either yours or his min= d > > > - I'll address that once I've read 5/5). > > > > > > The big concerns here are that to do interesting fuzzing we'll need a= ) > > > sequences of multiple packets/packets and b) to fuzz-generate the > > > headers, including malformed ones. > > > > > > AIUI, logically each fuzzer generated case could be run in a separate > > > instance of passt: the __AFL_LOOP() stuff is an optimization to avoid > > > the delay of a fresh startup on each cycle. Is that correct? > > > > yes, without __AFL_LOOP(), AFL++ forks a fresh passt for each input. > > Understood. > > > > > 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 +=3D -DVALGRIND > > > > valgrind: BASE_CFLAGS +=3D -g > > > > valgrind: all > > > > > > > > +FUZZ_CC ?=3D afl-clang-fast > > > > + > > > > +.PHONY: fuzz > > > > + > > > > +fuzz: > > > > + $(MAKE) clean > > > > + $(MAKE) CC=3D"$(FUZZ_CC)" CPPFLAGS=3D"-DFUZZING -DNDEBUG" > CFLAGS=3D"-g > > > -fsanitize=3Daddress" passt > > > > > > I'd recommend building the fuzzing binary under a different name, to > > > make accidentally using the wrong one a bit less likely. > > > > > > > .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 =3D read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 := 0) > > > > > > This macro ignores its parameter. Is that intentional? > > > > > > > Yes, this is intentional as it helps compile afl++ without > afl-clang-fast. > > more about this: > > > https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/RE= ADME.persistent_mode.md#2-tldr > > Weird, ok. > > > > > +# 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 =3D open(FUZZ_TURN_PATH, O_RDWR); > > > > > > FUZZ_TURN_PATH was defined in 1/5 but only used here, which makes > > > review harder. I'd suggest moving the definition to this patch. > > > > > > > Noted !! > > > > > > > > > + if (fd < 0) > > > > + return NULL; > > > > + > > > > + t =3D mmap(NULL, sizeof(*t), PROT_READ | PROT_WRITE, MAP_SHAR= ED, > fd, > > > 0); > > > > + close(fd); > > > > + > > > > + return (t =3D=3D MAP_FAILED) ? NULL : t; > > > > +} > > > > + > > > > +#endif > > > > + > > > > char pkt_buf[PKT_BUF_BYTES] __attribute__ ((aligned(PAGE_SIZE))); > > > > > > > > struct ctx passt_ctx =3D { > > > > @@ -282,9 +327,17 @@ static void passt_worker(void *opaque, int nfd= s, > > > struct epoll_event *events) > > > > icmp_sock_handler(c, ref, &now); > > > > break; > > > > case EPOLL_TYPE_VHOST_CMD: > > > > +#ifdef FUZZING > > > > + if (!c->vdev) > > > > + break; > > > > +#endif > > > > > > This serves a very similar purpose to the checks in 2/5, and the > > > comments I had there apply here as well. If we ignore an event here, > > > it means we're now on a path that's not really interesting to fuzz. > > > So instead of ignoring and carrying on, it would be better to mark > > > this as "program died correctly" and proceed to the next case. > > > > > > > Noted. > > > > > > > > > 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 =3D fuzz_turn_connect(); > > > > + > > > > +#define FUZZ_LOOP_ITERATIONS 10000 > > > > > > AFAICT, this has no effect, since __AFL_LOOP() ignores its parameter. > > > > > > > +#define FUZZ_DRAIN_BUF_SIZE 1600 > > > > + > > > > +#ifdef __AFL_HAVE_MANUAL_CONTROL > > > > + __AFL_INIT(); > > > > > > Both the definition and use of __AFL_INIT() are conditional on > > > __AFL_HAVE_MANUAL_CONTROL. Would it make more sense to define > > > __AFL_INIT() as a no-op if !__AFL_HAVE_MANUAL_CONTROL to avoid a > > > second #ifdef? > > > > > > I guess yes. Noted !! > > > > > > > > +#endif > > > > + { > > > > + unsigned char *buf =3D __AFL_FUZZ_TESTCASE_BUF; > > > > + > > > > + while (__AFL_LOOP(FUZZ_LOOP_ITERATIONS)) { > > > > + int len =3D __AFL_FUZZ_TESTCASE_LEN; > > > > + int injected =3D 0; > > > > + int pkt_len, round; > > > > + struct epoll_event ev; > > > > + union epoll_ref ref; > > > > + int min_pkt =3D 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(); > > > > > > flow_init() wipes the table itself, but doesn't clean up any existing > > > flows. If you're creating real external sockets, that means those > > > will be leaked, which means you could well hit the file descriptor > > > limit during a long fuzzing session. > > > > > > Also, it looks like flow_init() doesn't reset flow_first_free. > > > > > > > > > Seeing the structure of the afl loop, I now have further thoughts on > > > the assert()s you were suppressing earlier in the series. As I said, > > > if we hit those we want to stop this fuzzing path - it's no longer > > > interesting - but we don't want to mark it as a bug. A die() might > > > accomplish that, but of course would mean restarting passt, bypassing > > > the acceleration that __AFL_LOOP() is supposed to provide. > > > > > > Essentially what you want in those cases is to abort whatever you're > > > doing and continue on to the next iteration of the AFL loop. This > > > might make it one of the rare cases where setjmp() / longjmp() is a > > > good idea. > > > > > > > + /* Recreate epoll instance */ > > > > + close(c->epollfd); > > > > + c->epollfd =3D epoll_create1(EPOLL_CLOEXEC); > > > > + flow_epollid_register(EPOLLFD_ID_DEFAULT, > > > c->epollfd); > > > > + > > > > + if (c->fd_tap >=3D 0) { > > > > + union epoll_ref tref =3D { > > > > + .type =3D EPOLL_TYPE_TAP_PASS= T, > > > > + .fd =3D 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); > > > > > > You could use MSG_TRUNC here to avoid the need for a drain buffer. > > > > > > > + } > > > > + > > > > + /* Read epoll event from AFL++ buffer */ > > > > + memcpy(&ev, buf, sizeof(ev)); > > > > + ref =3D *((union epoll_ref *)&ev.data.u64); > > > > + > > > > + /* Set recv payload in AFL++ shared memory */ > > > > + fuzz_recv_data =3D buf + FUZZ_RECV_OFF; > > > > + fuzz_recv_data_len =3D > > > > + (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 =3D=3D EPOLL_TYPE_TAP_PASST || > > > > + ref.type =3D=3D 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 =3D len - (int)sizeof(ev); > > > > > > How does this differ from fuzz_recv_data_len? > > > > > > > pkt_len has the size of the TAP packet injected. > > > > fuzz_recv_data_len contains the size of the recv payload available to t= he > > determinstic > > fuzz_recv()/fuzz_recvmsg() wrappers. It starts at offset 12 and can go > upto > > 64KB. > > Ok, but they both have the same value of (len - sizeof(ev)). The > fuzz_recv_data_len case checks some more edge cases and uses different > defines, but it will mostly work out to the same thing. That seems > odd. > > > > > > > > + if (pkt_len > 0) > > > > + memcpy(pkt_buf, buf + > sizeof(ev), > > > > + pkt_len); > > > > + if (pkt_len < min_pkt) > > > > + pkt_len =3D min_pkt; > > > > + > > > > + /* construct ethernet header */ > > > > + eh =3D (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 =3D htons(ETH_P_IP); > > > > + > > > > + /* construct IPv4 header */ > > > > + iph =3D (struct iphdr *)(pkt_buf + > > > sizeof(*eh)); > > > > + iph->version =3D 4; > > > > + iph->ihl =3D 5; > > > > + iph->protocol =3D IPPROTO_TCP; > > > > + iph->saddr =3D c->ip4.addr.s_addr; > > > > + iph->daddr =3D c->ip4.guest_gw.s_addr= ; > > > > + iph->tot_len =3D htons(pkt_len - > > > sizeof(*eh)); > > > > + > > > > + /* Fix TCP Header */ > > > > + th =3D (struct tcphdr *)(pkt_buf + > > > sizeof(*eh) + > > > > + > > > sizeof(*iph)); > > > > + th->dest =3D htons(9999); > > > > + if (th->doff < 5) > > > > + th->doff =3D 5; > > > > > > As Stefano also points out, this is constructing a fixed version of > > > exactly the things we most want to fuzz. > > > > > > > + data =3D IOV_TAIL_FROM_BUF(pkt_buf, > pkt_len, > > > 0); > > > > + tap_add_packet(c, &data, &now); > > > > + tap_handler(c, &now); > > > > + injected =3D 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) !=3D > > > 0); > > > > + } > > > > > > I don't really understand what this 'turn' thing is doing. > > > > > > > "turn" flag is being used to synchronized the frame exchange between > passt > > and fuzz-server over the UNIX socket. > > I figured, but can you elaborate on how exactly it does that. > turn =3D 0 (passt's turn) turn =3D 1 (test-server's turn) -> Once passt inject the TAP packet, it sets "turn =3D 1" using atomic stor= e which tells the test-server that passt has sent something and now it can perform the read opr (as turn flag is mmap'd by both passt and fuzz-server). -> passt then does spin-wait until it's turn !=3D 0. -> similary test-server is spinning on it's own, checking if (turn =3D=3D = 1). When it sees 1, it does recv() on the UNIX socket to read the frame passt sent, generates a protocol response (e.g., ARP reply, SYN-ACK), sends it back via send() on the same socket, then sets turn =3D 0. -> Once passt sees turn =3D 0, it exit its spin-wait and continue processin= g. Without this turn flag, there was a timing problem: passt could call passt_worker() before test-server had a chance to read the outbound frame and send it's response back. The turn flag guarantees the response is available before passt tries to process it. > > > > + > > > > + passt_worker(c, 1, &ev); > > > > + > > > > + /* Process host-side TCP events */ > > > > + for (round =3D 0; round < 4; round++) { > > > > + nfds =3D epoll_wait(c->epollfd, event= s, > > > > + NUM_EPOLL_EVENTS, 0= ); > > > > + if (nfds <=3D 0) > > > > + break; > > > > + passt_worker(c, nfds, events); > > > > + } > > > > + > > > > + post_handler(c, &now); > > > > > > post_handler() is already called from passt_worker(), why do we need > > > another call? > > > > > > > + } > > > > + } > > > > + 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 > > > > > > > > > > -- > > > 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 > > > > > > > > > -- > > Anshu > > -- > 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 wa= y > | around. > http://www.ozlabs.org/~dgibson > --=20 Anshu --000000000000b37f8f06593a9682 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


On Mon, Aug 17,= 2026 at 9:16=E2=80=AFAM David Gibson <david@gibson.dropbear.id.au> wrote:
On Fri, Aug 14, 2026 at 04:02:58PM= +0530, Anshu Kumari wrote:
> On Fri, Aug 14, 2026 at 7:27=E2=80=AFAM David Gibson <david@gibson.dropbear.i= d.au>
> wrote:
>
> > On Wed, Aug 12, 2026 at 12:56:27PM +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:
> > >=C2=A0 =C2=A0- Resets deterministic clock, flow table, and ep= oll instance.
> > >=C2=A0 =C2=A0- Drains stale data from the TAP socket.
> > >=C2=A0 =C2=A0- Reads an epoll event from the AFL++ buffer. > > >=C2=A0 =C2=A0- For TAP events: constructs a packet with fixed= L2/L3/L4
> > >=C2=A0 =C2=A0 =C2=A0headers and injects it via tap_add_packet= () + tap_handler().
> > >=C2=A0 =C2=A0- Exchanges a turn flag with the test server for=
> > >=C2=A0 =C2=A0 =C2=A0bidirectional flow over the UNIX socket.<= br> > > >=C2=A0 =C2=A0- Calls passt_worker() to process the event.
> > >=C2=A0 =C2=A0- Polls for host-side TCP events via epoll_wait(= ).
> > >=C2=A0 =C2=A0- Runs post_handler() for deferred work.
> > >
> > > Added the 'make fuzz' target which builds passt with=
> > > afl-clang-fast, -DFUZZING, -DNDEBUG, and AddressSanitizer. > >
> > Stefano's concerns generally seconded (although I haven't= really got
> > my head around the role of the test server in either yours or his= mind
> > - I'll address that once I've read 5/5).
> >
> > The big concerns here are that to do interesting fuzzing we'l= l need a)
> > sequences of multiple packets/packets and b) to fuzz-generate the=
> > headers, including malformed ones.
> >
> > AIUI, logically each fuzzer generated case could be run in a sepa= rate
> > instance of passt: the __AFL_LOOP() stuff is an optimization to a= void
> > the delay of a fresh startup on each cycle.=C2=A0 Is that correct= ?
>
> yes, without __AFL_LOOP(), AFL++ forks a fresh passt for each input.
Understood.

> > > Signed-off-by: Anshu Kumari <anskuma@redhat.com>
> > > ---
> > >=C2=A0 Makefile |=C2=A0 =C2=A08 +++
> > >=C2=A0 passt.c=C2=A0 | 189 ++++++++++++++++++++++++++++++++++= +++++++++++++++++++++
> > >=C2=A0 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 +=3D -DVALGRIND=
> > >=C2=A0 valgrind: BASE_CFLAGS +=3D -g
> > >=C2=A0 valgrind: all
> > >
> > > +FUZZ_CC ?=3D afl-clang-fast
> > > +
> > > +.PHONY: fuzz
> > > +
> > > +fuzz:
> > > +=C2=A0 =C2=A0 =C2=A0$(MAKE) clean
> > > +=C2=A0 =C2=A0 =C2=A0$(MAKE) CC=3D"$(FUZZ_CC)" CPP= FLAGS=3D"-DFUZZING -DNDEBUG" CFLAGS=3D"-g
> > -fsanitize=3Daddress" passt
> >
> > I'd recommend building the fuzzing binary under a different n= ame, to
> > make accidentally using the wrong one a bit less likely.
> >
> > >=C2=A0 .PHONY: clean
> > >=C2=A0 clean:
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0$(RM) $(BIN) *~ *.o seccomp.h secc= omp_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 @@
> > >=C2=A0 #include <sys/prctl.h>
> > >=C2=A0 #include <netinet/if_ether.h>
> > >=C2=A0 #include <libgen.h>
> > > +#include <netinet/tcp.h>
> > >
> > >=C2=A0 #include "util.h"
> > >=C2=A0 #include "passt.h"
> > > @@ -54,12 +55,56 @@
> > >=C2=A0 #include "repair.h"
> > >=C2=A0 #include "netlink.h"
> > >=C2=A0 #include "epoll_ctl.h"
> > > +#include "flow_table.h"
> > > +#include "fuzz.h"
> > >
> > >=C2=A0 #define NUM_EPOLL_EVENTS=C2=A0 =C2=A0 =C2=A08
> > >
> > >=C2=A0 #define TIMER_INTERVAL_=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 MIN(TCP_TIMER_INTERVAL,
> > FWD_PORT_SCAN_INTERVAL)
> > >=C2=A0 #define TIMER_INTERVAL=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0MIN(TIMER_INTERVAL_,
> > FLOW_TIMER_INTERVAL)
> > >
> > > +#ifdef FUZZING
> > > +
> > > +/* AFL++ persistent mode / shared memory fuzzing compatibil= ity macros.
> > */
> > > +#ifndef __AFL_FUZZ_TESTCASE_LEN
> > > +=C2=A0 ssize_t=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 fuz= z_len;
> > > +=C2=A0 unsigned char=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 fuzz_buf[1024 * 1024];
> > > +# define __AFL_FUZZ_TESTCASE_LEN=C2=A0 =C2=A0 =C2=A0fuzz_le= n
> > > +# define __AFL_FUZZ_TESTCASE_BUF=C2=A0 =C2=A0 =C2=A0fuzz_bu= f
> > > +# define __AFL_FUZZ_INIT()=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0void sync(void)
> > > +# define __AFL_LOOP(x)=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0\
> > > +=C2=A0 =C2=A0 =C2=A0((fuzz_len =3D read(0, fuzz_buf, sizeof= (fuzz_buf))) > 0 ? 1 : 0)
> >
> > This macro ignores its parameter.=C2=A0 Is that intentional?
> >
>
> Yes, this is intentional as it=C2=A0 helps compile afl++ without afl-c= lang-fast.
> more about this:
> https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentat= ion/README.persistent_mode.md#2-tldr

Weird, ok.

> > > +# define __AFL_INIT()=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 sync()
> > > +#endif
> > > +
> > > +#ifdef __AFL_HAVE_MANUAL_CONTROL
> > > +=C2=A0 __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<= br> > > > + */
> > > +static struct fuzz_turn *fuzz_turn_connect(void)
> > > +{
> > > +=C2=A0 =C2=A0 =C2=A0struct fuzz_turn *t;
> > > +=C2=A0 =C2=A0 =C2=A0int fd;
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0fd =3D open(FUZZ_TURN_PATH, O_RDWR); > >
> > FUZZ_TURN_PATH was defined in 1/5 but only used here, which makes=
> > review harder.=C2=A0 I'd suggest moving the definition to thi= s patch.
> >
>
> Noted !!
>
> >
> > > +=C2=A0 =C2=A0 =C2=A0if (fd < 0)
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0return NULL= ;
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0t =3D mmap(NULL, sizeof(*t), PROT_READ = | PROT_WRITE, MAP_SHARED, fd,
> > 0);
> > > +=C2=A0 =C2=A0 =C2=A0close(fd);
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0return (t =3D=3D MAP_FAILED) ? NULL : t= ;
> > > +}
> > > +
> > > +#endif
> > > +
> > >=C2=A0 char pkt_buf[PKT_BUF_BYTES]=C2=A0 __attribute__ ((alig= ned(PAGE_SIZE)));
> > >
> > >=C2=A0 struct ctx passt_ctx =3D {
> > > @@ -282,9 +327,17 @@ static void passt_worker(void *opaque, = int nfds,
> > struct epoll_event *events)
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0icmp_sock_handler(c, ref, &now);
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0break;
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case E= POLL_TYPE_VHOST_CMD:
> > > +#ifdef FUZZING
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0if (!c->vdev)
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;
> > > +#endif
> >
> > This serves a very similar purpose to the checks in 2/5, and the<= br> > > comments I had there apply here as well.=C2=A0 If we ignore an ev= ent here,
> > it means we're now on a path that's not really interestin= g to fuzz.
> > So instead of ignoring and carrying on, it would be better to mar= k
> > this as "program died correctly" and proceed to the nex= t case.
> >
>
> Noted.
>
> >
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0vu_control_handler(c->vdev, c->fd_tap, eventm= ask);
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0break;
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case E= POLL_TYPE_VHOST_KICK:
> > > +#ifdef FUZZING
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0if (!c->vdev)
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break;
> > > +#endif
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0vu_kick_cb(c->vdev, ref, &now);
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0break;
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case E= POLL_TYPE_REPAIR_LISTEN:
> > > @@ -450,6 +503,141 @@ int main(int argc, char **argv)
> > >
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0timer_init(c, &now);
> > >
> > > +#ifdef FUZZING
> > > +=C2=A0 =C2=A0 =C2=A0fuzz_turn_ptr =3D fuzz_turn_connect();<= br> > > > +
> > > +#define FUZZ_LOOP_ITERATIONS 10000
> >
> > AFAICT, this has no effect, since __AFL_LOOP() ignores its parame= ter.
> >
> > > +#define FUZZ_DRAIN_BUF_SIZE=C2=A0 1600
> > > +
> > > +#ifdef __AFL_HAVE_MANUAL_CONTROL
> > > +=C2=A0 =C2=A0 =C2=A0__AFL_INIT();
> >
> > Both the definition and use of __AFL_INIT() are conditional on > > __AFL_HAVE_MANUAL_CONTROL.=C2=A0 Would it make more sense to defi= ne
> > __AFL_INIT() as a no-op if !__AFL_HAVE_MANUAL_CONTROL to avoid a<= br> > > second #ifdef?
> >
> > I guess yes. Noted !!
>
>
> > > +#endif
> > > +=C2=A0 =C2=A0 =C2=A0{
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0unsigned ch= ar *buf =3D __AFL_FUZZ_TESTCASE_BUF;
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0while (__AF= L_LOOP(FUZZ_LOOP_ITERATIONS)) {
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0int len =3D __AFL_FUZZ_TESTCASE_LEN;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0int injected =3D 0;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0int pkt_len, round;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0struct epoll_event ev;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0union epoll_ref ref;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0int min_pkt =3D sizeof(struct ethhdr) +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sizeof(st= ruct iphdr) +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sizeof(st= ruct tcphdr);
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0if (len < (int)sizeof(ev))
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0continue;
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0/* Reset clock, flow table and epoll for each
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 * AFL++ iteration.
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 */
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0fuzz_clock_reset();
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0clock_gettime(CLOCK_MONOTONIC, &now);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0timer_init(c, &now);
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0flow_init();
> >
> > flow_init() wipes the table itself, but doesn't clean up any = existing
> > flows.=C2=A0 If you're creating real external sockets, that m= eans those
> > will be leaked, which means you could well hit the file descripto= r
> > limit during a long fuzzing session.
> >
> > Also, it looks like flow_init() doesn't reset flow_first_free= .
> >
> >
> > Seeing the structure of the afl loop, I now have further thoughts= on
> > the assert()s you were suppressing earlier in the series.=C2=A0 A= s I said,
> > if we hit those we want to stop this fuzzing path - it's no l= onger
> > interesting - but we don't want to mark it as a bug.=C2=A0 A = die() might
> > accomplish that, but of course would mean restarting passt, bypas= sing
> > the acceleration that __AFL_LOOP() is supposed to provide.
> >
> > Essentially what you want in those cases is to abort whatever you= 're
> > doing and continue on to the next iteration of the AFL loop.=C2= =A0 This
> > might make it one of the rare cases where setjmp() / longjmp() is= a
> > good idea.
> >
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0/* Recreate epoll instance */
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0close(c->epollfd);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0c->epollfd =3D epoll_create1(EPOLL_CLOEXEC);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0flow_epollid_register(EPOLLFD_ID_DEFAULT,
> > c->epollfd);
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0if (c->fd_tap >=3D 0) {
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0union epoll_ref tref =3D {
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0.t= ype =3D EPOLL_TYPE_TAP_PASST,
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0.f= d =3D c->fd_tap
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0};
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0epoll_add(c->epollfd,
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0EPOLLIN | EPOLLRDHUP, tref);
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Drain stale socket data */<= br> > > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0char drain[FUZZ_DRAIN_BUF_SIZE= ];
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0while (recv(c->fd_tap, drai= n,
> > sizeof(drain),
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0MSG_DONTWAIT) > 0);
> >
> > You could use MSG_TRUNC here to avoid the need for a drain buffer= .
> >
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0}
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0/* Read epoll event from AFL++ buffer */
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0memcpy(&ev, buf, sizeof(ev));
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0ref =3D *((union epoll_ref *)&ev.data.u64);
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0/* Set recv payload in AFL++ shared memory */
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0fuzz_recv_data =3D buf + FUZZ_RECV_OFF;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0fuzz_recv_data_len =3D
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(len > FUZZ_RECV_OFF + FUZZ= _RECV_MAX)
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 ? FUZZ_RECV_MAX
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 : ((len > FUZZ_RECV_OFF)
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0? len - FUZZ_RECV_OFF : 0);
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0/* Inject fuzz packet for TAP events */
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0if (ref.type =3D=3D EPOLL_TYPE_TAP_PASST ||
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ref.type =3D=3D EPOLL_TYPE_TAP_PASTA) {
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0struct iov_tail data;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0struct ethhdr *eh;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0struct iphdr *iph;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0struct tcphdr *th;
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0tap_flush_pools();
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0memset(pkt_buf, 0, min_pkt); > > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0pkt_len =3D len - (int)sizeof(= ev);
> >
> > How does this differ from fuzz_recv_data_len?
> >
>
> pkt_len has the size of the TAP packet injected.
>
> fuzz_recv_data_len contains the size of the recv payload available to = the
> determinstic
> fuzz_recv()/fuzz_recvmsg() wrappers. It starts at offset 12 and can go= upto
> 64KB.

Ok, but they both have the same value of (len - sizeof(ev)).=C2=A0 The
fuzz_recv_data_len case checks some more edge cases and uses different
defines, but it will mostly work out to the same thing.=C2=A0 That seems odd.

> >
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (pkt_len > 0)
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0me= mcpy(pkt_buf, buf + sizeof(ev),
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 pkt_len);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (pkt_len < min_pkt)
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0pk= t_len =3D min_pkt;
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* construct ethernet header *= /
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0eh =3D (struct ethhdr *)pkt_bu= f;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0memcpy(eh->h_dest, c->ou= r_tap_mac,
> > ETH_ALEN);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0memcpy(eh->h_source, c->= guest_mac,
> > ETH_ALEN);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0eh->h_proto =3D htons(ETH_P= _IP);
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* construct IPv4 header */ > > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0iph =3D (struct iphdr *)(pkt_b= uf +
> > sizeof(*eh));
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0iph->version =3D 4;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0iph->ihl =3D 5;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0iph->protocol =3D IPPROTO_T= CP;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0iph->saddr =3D c->ip4.ad= dr.s_addr;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0iph->daddr =3D c->ip4.gu= est_gw.s_addr;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0iph->tot_len =3D htons(pkt_= len -
> > sizeof(*eh));
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Fix TCP Header */
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0th =3D (struct tcphdr *)(pkt_b= uf +
> > sizeof(*eh) +
> > > +
> > sizeof(*iph));
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0th->dest =3D htons(9999); > > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (th->doff < 5)
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0th= ->doff =3D 5;
> >
> > As Stefano also points out, this is constructing a fixed version = of
> > exactly the things we most want to fuzz.
> >
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0data =3D IOV_TAIL_FROM_BUF(pkt= _buf, pkt_len,
> > 0);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0tap_add_packet(c, &data, &= amp;now);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0tap_handler(c, &now);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0injected =3D 1;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0}
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0/* Turn exchange -- only if data was sent */
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0if (injected && fuzz_turn_ptr) {
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0__atomic_store_n(&fuzz_tur= n_ptr->turn, 1,
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 __ATOMIC_RELEASE);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0while
> > (__atomic_load_n(&fuzz_turn_ptr->turn,
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0__ATOMIC_ACQUIRE) !=3D
> > 0);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0}
> >
> > I don't really understand what this 'turn' thing is d= oing.
> >
>
> "turn" flag is being used to synchronized the frame exchange= between passt
> and fuzz-server over the UNIX socket.

I figured, but can you elaborate on how exactly it does that.

turn =3D 0 (passt's turn)
turn =3D 1 (= test-server's turn)

-> Once passt inject the TAP p= acket, it sets "turn =3D 1" using atomic store which tells
<= div>=C2=A0 =C2=A0 the test-server that passt has sent something and now it = can perform the read opr (as
=C2=A0 =C2=A0 turn flag is mmap'= d by both passt and fuzz-server).=C2=A0

-> passt then = does spin-wait until it's turn !=3D 0.=C2=A0

-= > similary test-server is spinning on it's own,=C2=A0=C2=A0checking = if (turn =3D=3D 1). When it sees 1,
=C2=A0 =C2=A0 it does recv() = on the UNIX socket to read the frame passt sent,
=C2=A0 =C2=A0 ge= nerates a protocol response (e.g., ARP reply, SYN-ACK),
=C2=A0 = =C2=A0 sends it back via send() on the same socket, then sets turn =3D 0.

-> Once passt sees turn =3D 0, it exit its spin-= wait and continue processing.

Without this turn fl= ag, there was a timing problem: passt could call passt_worker()
b= efore test-server had a chance to read the outbound frame and send it's= response back.
The turn flag guarantees the response is availabl= e before passt tries to process it.


> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0passt_worker(c, 1, &ev);
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0/* Process host-side TCP events */
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0for (round =3D 0; round < 4; round++) {
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0nfds =3D epoll_wait(c->epol= lfd, events,
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0NUM_EPOLL_EVENTS, 0);
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0if (nfds <=3D 0)
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0br= eak;
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0passt_worker(c, nfds, events);=
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0}
> > > +
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0post_handler(c, &now);
> >
> > post_handler() is already called from passt_worker(), why do we n= eed
> > another call?
> >
> > > +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}
> > > +=C2=A0 =C2=A0 =C2=A0}
> > > +=C2=A0 =C2=A0 =C2=A0return 0;
> > > +#else
> > >=C2=A0 loop:
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0/* NOLINTBEGIN(bugprone-branch-clo= ne): intervals can be the same */
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0/* cppcheck-suppress [duplicateVal= ueTernary, unmatchedSuppression]
> > */
> > > @@ -461,4 +649,5 @@ loop:
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0passt_worker(c, nfds, events);
> > >
> > >=C2=A0 =C2=A0 =C2=A0 =C2=A0goto loop;
> > > +#endif /* FUZZING */
> > >=C2=A0 }
> > > --
> > > 2.55.0
> > >
> >
> > --
> > David Gibson (he or they)=C2=A0 =C2=A0 =C2=A0 =C2=A0| I'll ha= ve my music baroque, and my code
> > david AT gibson.dropbear.id.au=C2=A0 | minimalist, thank y= ou, not the other way
> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0| around.
> > http://www.ozlabs.org/~dgibson
> >
>
>
> --
> Anshu

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


--
Anshu
--000000000000b37f8f06593a9682--