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=CV3BIRFM; 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 3F7C75A0272 for ; Mon, 28 Sep 2026 07:17:42 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1790572661; 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=y+GR1tgmtjVlDBYnNTGbKOMdZ6XHr3/kYlBfVwPpjKY=; b=CV3BIRFMGqIRbd91IIt3fOeD4QoTNrNEX4zI9+WsedcPidCyycT52YxLwEQJoUcU1c5Y3W NPv+KRqKUi1+6yBpGZ6z7OTc/mBjm4GYjiIG8X780AuvbAbepapjCbuslKcM6suM+kdwaU ObUi550SdHAcDIyLOEkWzoYySMe2VvU= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-574-ogBOuUCPNf6VHpY7dOgX3w-1; Mon, 28 Sep 2026 01:17:36 -0400 X-MC-Unique: ogBOuUCPNf6VHpY7dOgX3w-1 X-Mimecast-MFC-AGG-ID: ogBOuUCPNf6VHpY7dOgX3w_1790572655 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id A195E1977517; Mon, 28 Sep 2026 05:17:35 +0000 (UTC) Received: from anskuma-thinkpadp1gen7.bengluru.csb (unknown [10.74.80.83]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 48D0F19560AB; Mon, 28 Sep 2026 05:17:32 +0000 (UTC) From: Anshu Kumari To: sbrivio@redhat.com, passt-dev@passt.top Subject: [PATCH v2 1/7] fuzz: Add AFL++ shared memory testcase buffer layout Date: Mon, 28 Sep 2026 10:47:21 +0530 Message-ID: <20260928051727.2251281-2-anskuma@redhat.com> In-Reply-To: <20260928051727.2251281-1-anskuma@redhat.com> References: <20260928051727.2251281-1-anskuma@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: S1LkF7KAcVazl52WsUGRBQ6jQf894P6Ii55qiyJfM00_1790572655 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit content-type: text/plain; charset="US-ASCII"; x-default=true Message-ID-Hash: IEGYQDJXWG4MRVDBXLCGPKUGX7I6HLAI X-Message-ID-Hash: IEGYQDJXWG4MRVDBXLCGPKUGX7I6HLAI 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: lvivier@redhat.com, anskuma@redhat.com, abdobngad@gmail.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: Define the shared memory layout that both passt and the test server use to interpret AFL++ testcase data. Each testcase starts with a 10-byte header carrying explicit lengths: [0..3] u32 n_events epoll events to inject [4..5] u16 tap_len L2 tap frame data [6..7] u16 testbuf_len test-server payload [8..9] u16 sockopt_len getsockopt() overrides followed by four variable-length payload regions: (a) events: simulated epoll events for passt's main loop (b) tap: length-prefixed raw L2 frames (c) testbuf: payload the test server sends on connections (d) sockopt: fuzzer-controlled TCP_INFO data fuzz_parse_layout() reads the header and computes each region's offset and length. Signed-off-by: Anshu Kumari --- Makefile | 13 ++--- fuzz-testbuf.h | 135 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 fuzz-testbuf.h diff --git a/Makefile b/Makefile index b3152425..97c27f7c 100644 --- a/Makefile +++ b/Makefile @@ -47,12 +47,13 @@ SRCS = $(PASST_SRCS) $(PASST_REPAIR_SRCS) $(PESTO_SRCS) MANPAGES = passt.1 pasta.1 pesto.1 passt-repair.1 PASST_HEADERS = arch.h arp.h bitmap.h checksum.h conf.h dhcp.h dhcpv6.h \ - epoll_ctl.h flow.h fwd.h fwd_rule.h flow_table.h icmp.h icmp_flow.h \ - inany.h iov.h ip.h isolation.h lineread.h linux_dep.h log.h migrate.h \ - ndp.h netlink.h packet.h parse.h passt.h pasta.h pcap.h pif.h repair.h \ - serialise.h siphash.h tap.h tcp.h tcp_buf.h tcp_conn.h tcp_internal.h \ - tcp_splice.h tcp_vu.h udp.h udp_flow.h udp_internal.h udp_vu.h util.h \ - vhost_user.h virtio.h vu_common.h + epoll_ctl.h flow.h fwd.h fwd_rule.h flow_table.h fuzz-testbuf.h \ + icmp.h icmp_flow.h inany.h iov.h ip.h isolation.h lineread.h \ + linux_dep.h log.h migrate.h ndp.h netlink.h packet.h parse.h \ + passt.h pasta.h pcap.h pif.h repair.h serialise.h siphash.h tap.h \ + tcp.h tcp_buf.h tcp_conn.h tcp_internal.h tcp_splice.h tcp_vu.h \ + udp.h udp_flow.h udp_internal.h udp_vu.h util.h vhost_user.h \ + virtio.h vu_common.h PASST_REPAIR_HEADERS = linux_dep.h PESTO_HEADERS = bitmap.h common.h fwd_rule.h inany.h ip.h log.h parse.h \ pesto.h serialise.h diff --git a/fuzz-testbuf.h b/fuzz-testbuf.h new file mode 100644 index 00000000..6366528f --- /dev/null +++ b/fuzz-testbuf.h @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* fuzz-testbuf.h - AFL++ testcase buffer layout shared between passt + * and the test server. + * + * Copyright Red Hat + * Author: Anshu Kumari + */ + +#ifndef FUZZ_TESTBUF_H +#define FUZZ_TESTBUF_H + +#include +#include +#include + +/* + * AFL++ testcase buffer layout + * + * Each fuzz iteration receives a single flat buffer split into a + * fixed 10-byte header followed by four variable-length regions: + * + * Header (FUZZ_HDR_SIZE = 10 bytes): + * [0..3] u32 n_events number of epoll events + * [4..5] u16 tap_len bytes of tap (L2 frame) data + * [6..7] u16 testbuf_len bytes of test-server payload + * [8..9] u16 sockopt_len bytes of getsockopt() overrides + * + * Payload (starts at offset FUZZ_HDR_SIZE): + * (a) events: n_events * sizeof(struct epoll_event) + * Simulated epoll events consumed by passt. + * (b) tap: tap_len bytes of length-prefixed L2 frames + * injected into passt as tap input. + * (c) testbuf: testbuf_len bytes consumed only by the + * test server to send load to passt. + * (d) sockopt: sockopt_len bytes of fuzzer-controlled + * TCP_INFO data returned by getsockopt(). + * + */ +#define FUZZ_EV_COUNT_OFF 0 +#define FUZZ_TAP_LEN_OFF 4 +#define FUZZ_TESTBUF_LEN_OFF 6 +#define FUZZ_SOCKOPT_LEN_OFF 8 +#define FUZZ_HDR_SIZE 10 + +#define FUZZ_TAP_MAX (64 * 1024) +#define FUZZ_TESTBUF_MAX (64 * 1024) +#define FUZZ_SOCKOPT_MAX 256 + +/** + * struct fuzz_layout - testcase layout + * @n_events: Number of epoll events in region (a) + * @tap_off: Byte offset of region (b) in the testcase + * @tap_len: Byte length of region (b) + * @testbuf_off: Byte offset of region (c) in the testcase + * @testbuf_len: Byte length of region (c) + * @sockopt_off: Byte offset of region (d) in the testcase + * @sockopt_len: Byte length of region (d) + */ +struct fuzz_layout { + uint32_t n_events; + uint32_t tap_off; + uint32_t tap_len; + uint32_t testbuf_off; + uint32_t testbuf_len; + uint32_t sockopt_off; + uint32_t sockopt_len; +}; + +/** + * fuzz_parse_layout() - Split a testcase into non-overlapping regions + * @buf: Raw AFL++ testcase buffer + * @total_len: Total byte length of @buf + * + * Each AFL++ testcase starts with a 10-byte header that says how + * large each payload region should be. This function reads that + * header and figures out where each region actually starts and + * ends, making sure nothing runs past the end of the buffer and + * no two regions overlap. + * + * Return: a fuzz_layout with offsets and lengths for every region, + * or all zeros if the buffer is too small for the header + */ +static inline struct fuzz_layout fuzz_parse_layout(const uint8_t *buf, + uint32_t total_len) +{ + struct fuzz_layout l = { 0 }; + uint32_t raw_n_events; + uint32_t remaining; + uint16_t raw16; + + if (total_len < FUZZ_HDR_SIZE) + return l; + + memcpy(&raw_n_events, buf + FUZZ_EV_COUNT_OFF, sizeof(raw_n_events)); + + if (total_len > FUZZ_HDR_SIZE) { + uint32_t ev_space = total_len - FUZZ_HDR_SIZE; + uint32_t max_ev = ev_space / sizeof(struct epoll_event); + + l.n_events = raw_n_events > max_ev ? max_ev : raw_n_events; + } + + l.tap_off = FUZZ_HDR_SIZE + l.n_events * sizeof(struct epoll_event); + remaining = total_len > l.tap_off ? total_len - l.tap_off : 0; + + memcpy(&raw16, buf + FUZZ_TAP_LEN_OFF, sizeof(raw16)); + l.tap_len = raw16; + if (l.tap_len > FUZZ_TAP_MAX) + l.tap_len = FUZZ_TAP_MAX; + if (l.tap_len > remaining) + l.tap_len = remaining; + remaining -= l.tap_len; + + l.testbuf_off = l.tap_off + l.tap_len; + memcpy(&raw16, buf + FUZZ_TESTBUF_LEN_OFF, sizeof(raw16)); + l.testbuf_len = raw16; + if (l.testbuf_len > FUZZ_TESTBUF_MAX) + l.testbuf_len = FUZZ_TESTBUF_MAX; + if (l.testbuf_len > remaining) + l.testbuf_len = remaining; + remaining -= l.testbuf_len; + + l.sockopt_off = l.testbuf_off + l.testbuf_len; + memcpy(&raw16, buf + FUZZ_SOCKOPT_LEN_OFF, sizeof(raw16)); + l.sockopt_len = raw16; + if (l.sockopt_len > FUZZ_SOCKOPT_MAX) + l.sockopt_len = FUZZ_SOCKOPT_MAX; + if (l.sockopt_len > remaining) + l.sockopt_len = remaining; + + return l; +} + +#endif /* FUZZ_TESTBUF_H */ -- 2.55.0