// 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 */