Skip to content

Commit 9831bed

Browse files
committed
Add: ST fuzzing harnesses with logging + validation hooks
- add ST20/ST22/ST30/ST40 fuzz harnesses, shared Meson wiring, and pytest runner - enable DEBUG-level MTL logging within the fuzz binaries and surface detailed ST40 drop reasons - document how to build/run fuzzers (Meson/build.sh, pytest nightly marker, logging behavior) - clean up legacy enable_fuzzing_st40 options in build scripts - fix sample/ext_frame plane math and noctx handler assertions to reflect frame[0] usage - propagate pacing fixes and ancillary logging hooks inside rx/tx session code - refresh validation configs to capture fuzz logs and new helper assets
1 parent 3fb5a4b commit 9831bed

21 files changed

+1393
-11
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ An important point to note is that narrow pacing of TX is only supported for the
7878

7979
## 2. Build
8080

81-
Please refer to [Build Guide](doc/build.md) for instructions on how to build DPDK, the library, and the sample application.
81+
Please refer to [Build Guide](doc/build.md) for instructions on how to build DPDK, the library, and the sample application. Guidance for the fuzz targets lives in [doc/fuzzing.md](doc/fuzzing.md).
8282

8383
For Windows, please refer to the [Windows Build Guide](doc/build_WIN.md) for instructions on how to build.
8484

app/sample/ext_frame/rx_st20_pipeline_dyn_ext_frame_sample.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@ static int rx_st20p_query_ext_frame(void* priv, struct st_ext_frame* ext_frame,
4949
ext_frame->size = s->ext_frames[i].buf_len;
5050

5151
uint8_t* addr = ext_frame->addr[0];
52-
uint8_t planes = st_frame_fmt_planes(meta->fmt);
52+
enum st_frame_fmt frame_fmt = st_frame_fmt_from_transport(meta->fmt);
53+
uint8_t planes = st_frame_fmt_planes(frame_fmt);
5354
for (int plane = 0; plane < planes; plane++) {
5455
if (plane > 0)
5556
ext_frame->iova[plane] =
5657
ext_frame->iova[plane - 1] + ext_frame->linesize[plane - 1] * meta->height;
57-
ext_frame->linesize[plane] = st_frame_least_linesize(meta->fmt, meta->width, plane);
58+
ext_frame->linesize[plane] = st_frame_least_linesize(frame_fmt, meta->width, plane);
5859
ext_frame->addr[plane] = addr;
5960
addr += ext_frame->linesize[plane] * meta->height;
6061
}

build.sh

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,20 @@ set -e
88
user=$(whoami)
99

1010
function usage() {
11-
echo "Usage: $0 [debug/debugoptimized/plain/release]"
11+
echo "Usage: $0 [debug|debugonly|debugoptimized|plain|release] [enable_fuzzing]"
1212
exit 0
1313
}
1414

1515
buildtype=release
1616
enable_asan=false
1717
enable_tap=false
1818
enable_usdt=true
19+
enable_fuzzing=false
1920

2021
: "${MTL_BUILD_ENABLE_ASAN:=false}"
2122
: "${MTL_BUILD_ENABLE_TAP:=false}"
2223
: "${MTL_BUILD_DISABLE_USDT:=false}"
24+
: "${MTL_BUILD_ENABLE_FUZZING:=false}"
2325

2426
if [ "$MTL_BUILD_ENABLE_ASAN" == "true" ]; then
2527
enable_asan=true
@@ -37,7 +39,12 @@ if [ "$MTL_BUILD_DISABLE_USDT" == "true" ]; then
3739
echo "Disable USDT"
3840
fi
3941

40-
if [ -n "$1" ]; then
42+
if [ "$MTL_BUILD_ENABLE_FUZZING" == "true" ]; then
43+
enable_fuzzing=true
44+
echo "Enable fuzzers"
45+
fi
46+
47+
while [ $# -gt 0 ]; do
4148
case $1 in
4249
"debug")
4350
buildtype=debug
@@ -55,11 +62,16 @@ if [ -n "$1" ]; then
5562
"release")
5663
buildtype=release
5764
;;
65+
"enable_fuzzing" | "--enable-fuzzing")
66+
enable_fuzzing=true
67+
echo "Enable fuzzers"
68+
;;
5869
*)
5970
usage
6071
;;
6172
esac
62-
fi
73+
shift
74+
done
6375

6476
WORKSPACE=$PWD
6577
LIB_BUILD_DIR=${WORKSPACE}/build
@@ -71,7 +83,7 @@ MANAGER_BUILD_DIR=${WORKSPACE}/build/manager
7183
RXTXAPP_BUILD_DIR=${WORKSPACE}/tests/tools/RxTxApp/build
7284

7385
# build lib
74-
meson setup "${LIB_BUILD_DIR}" -Dbuildtype="$buildtype" -Denable_asan="$enable_asan" -Denable_tap="$enable_tap" -Denable_usdt="$enable_usdt"
86+
meson setup "${LIB_BUILD_DIR}" -Dbuildtype="$buildtype" -Denable_asan="$enable_asan" -Denable_tap="$enable_tap" -Denable_usdt="$enable_usdt" -Denable_fuzzing="$enable_fuzzing"
7587
pushd "${LIB_BUILD_DIR}"
7688
ninja
7789
if [ "$user" == "root" ] || [ "$OS" == "Windows_NT" ]; then

doc/fuzzing.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# ST40/ST30/ST20/ST22 RX Fuzzing
2+
3+
This repository now provides libFuzzer harnesses that exercise the runtime ST40 RX
4+
ancillary packet path, the ancillary helper utilities, the ST30 RX audio frame path,
5+
and the ST20/ST22 RX video frame pipelines. The harnesses are built only when the
6+
dedicated Meson option is enabled because they pull in DPDK and need an EAL
7+
environment.
8+
9+
## Build Requirements
10+
11+
- Clang/LLVM with `-fsanitize=fuzzer` support **or** a standalone `libFuzzer` library.
12+
- DPDK must already be available, as required by the main library build.
13+
- Linux is recommended; the harnesses rely on EAL flags such as `--no-huge` and
14+
`--in-memory`.
15+
16+
## Enabling The Harnesses
17+
18+
You can turn the fuzzers on either directly through Meson **or** via the top-level
19+
`build.sh` helper:
20+
21+
```sh
22+
meson setup build -Denable_fuzzing=true
23+
```
24+
25+
```sh
26+
./build.sh release enable_fuzzing
27+
# or export MTL_BUILD_ENABLE_FUZZING=true before invoking build.sh
28+
```
29+
30+
Add `-Denable_asan=true` (or `MTL_BUILD_ENABLE_ASAN=true`) if you also want
31+
AddressSanitizer instrumentation. Existing build directories can be reconfigured with
32+
`meson configure build -Denable_fuzzing=true`.
33+
34+
Meson injects the `MTL_ENABLE_FUZZING_ST40`, `MTL_ENABLE_FUZZING_ST30`,
35+
`MTL_ENABLE_FUZZING_ST20`, and `MTL_ENABLE_FUZZING_ST22` defines so the RX
36+
implementation exposes the minimal helper hooks required by the fuzzers.
37+
38+
## Building
39+
40+
After configuration simply run the standard build:
41+
42+
```sh
43+
ninja -C build
44+
```
45+
46+
The following fuzz targets will be produced inside `build/tests/fuzz/`:
47+
48+
- `st40_rx_rtp_fuzz` – feeds arbitrary RTP payloads through
49+
`rx_ancillary_session_handle_pkt`, using a lightweight DPDK/EAL bootstrap.
50+
- `st40_ancillary_helpers_fuzz` – targets the pure helper functions in
51+
`st_ancillary.c` (`st40_set_udw`, `st40_get_udw`, parity helpers, checksum, etc.).
52+
- `st30_rx_frame_fuzz` – injects fuzzed RFC3550 audio RTP packets into the
53+
frame-level ST30 RX path, allocating a tiny in-memory framebuffer to exercise the
54+
sequencing and framing logic.
55+
- `st20_rx_frame_fuzz` – drives the frame-level ST20 video RX path, including slot
56+
management, RTP parsing, bitmap tracking, and framebuffer assembly for
57+
RFC4175-formatted video payloads.
58+
- `st22_rx_frame_fuzz` – targets the ST22 codestream RX path, validating packetized
59+
codestream parsing, JPVS/COLR box handling, and codestream reassembly logic.
60+
61+
## Running
62+
63+
Each target is a standalone libFuzzer executable. A minimal invocation looks like:
64+
65+
```sh
66+
./build/tests/fuzz/st40_rx_rtp_fuzz -runs=1000 corpus_dir
67+
```
68+
69+
The harness configures EAL internally (`--no-huge --in-memory --no-shconf -c1 -n1`),
70+
so no additional environment setup is required beyond access to `/dev/hugepages` not
71+
being mandatory.
72+
73+
A similar invocation drives the ST30 frame harness:
74+
75+
```sh
76+
./build/tests/fuzz/st30_rx_frame_fuzz -max_total_time=60 corpus_audio
77+
```
78+
79+
And the new ST20/ST22 video harnesses can be exercised with:
80+
81+
```sh
82+
./build/tests/fuzz/st20_rx_frame_fuzz -max_total_time=60 corpus_video
83+
./build/tests/fuzz/st22_rx_frame_fuzz -max_total_time=60 corpus_codestream
84+
```
85+
86+
For long fuzzing sessions point the binary at a writable corpus directory. The helper
87+
fuzzer works the same way:
88+
89+
```sh
90+
./build/tests/fuzz/st40_ancillary_helpers_fuzz -max_total_time=60 corpus_helpers
91+
```
92+
93+
### Pytest integration
94+
95+
The validation suite drives every fuzz target with long-running libFuzzer passes and
96+
streams the combined libFuzzer/MTL output into
97+
`tests/validation/logs/latest/pytest.log`. Execute:
98+
99+
```sh
100+
pytest tests/validation/fuzzing/test_fuzzing.py -k fuzz
101+
```
102+
103+
Each test carries `@pytest.mark.nightly` and runs `-runs=500000` iterations by default
104+
(override via `MTL_FUZZ_TEST_RUNS`). That keeps quick developer test shards short while
105+
allowing the nightly job to exercise a deeper corpus.
106+
107+
### Logging during fuzzing
108+
109+
All harnesses automatically raise the MTL and DPDK log level to `DEBUG` and install a
110+
custom printer that forwards every log line to `stderr`. When invoked through pytest (or
111+
manually), you will see `MTL:`-prefixed lines for interesting events—payload-type / SSRC
112+
mismatches, redundant packets, enqueue failures, etc.—inside the test logs without any
113+
extra configuration.
114+
115+
## Notes
116+
117+
- The RX harness drains the session ring immediately after notifying the callback so
118+
mbufs are always released, which keeps memory usage stable even under ASan.
119+
- Inputs must be at least the size of an RFC8331 ancillary header (62 bytes) to ensure
120+
the handler never reads beyond the provided mbuf.
121+
- All harnesses cap their input size to fit inside a standard DPDK mbuf (2 KiB) to
122+
avoid oversized allocations during fuzzing.
123+
- The ST30 harness requires inputs large enough to contain an RFC3550 audio header
124+
(12 bytes). Packets larger than 2 KiB are truncated so they still fit within a
125+
single mbuf.
126+
- The ST20 harness expects RFC4175-style video payloads (12-byte RTP header plus
127+
video-specific extensions). Packets shorter than the combined header size are
128+
ignored.
129+
- The ST22 harness operates on RFC9134 codestream packets. Inputs must include at
130+
least the base JP2K header; larger payloads exercise the box parsing and codestream
131+
completion/marker logic.

lib/src/st2110/st_rx_ancillary_session.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
#include "../mt_stat.h"
1010
#include "st_ancillary_transmitter.h"
1111

12+
#ifdef MTL_ENABLE_FUZZING_ST40
13+
#define ST40_FUZZ_LOG(...) info(__VA_ARGS__)
14+
#else
15+
#define ST40_FUZZ_LOG(...) \
16+
do { \
17+
} while (0)
18+
#endif
19+
1220
/* call rx_ancillary_session_put always if get successfully */
1321
static inline struct st_rx_ancillary_session_impl* rx_ancillary_session_get(
1422
struct st_rx_ancillary_sessions_mgr* mgr, int idx) {
@@ -100,6 +108,10 @@ static int rx_ancillary_session_handle_pkt(struct mtl_main_impl* impl,
100108
uint32_t tmstamp = ntohl(rtp->tmstamp);
101109

102110
if (ops->payload_type && (payload_type != ops->payload_type)) {
111+
#ifdef MTL_ENABLE_FUZZING_ST40
112+
ST40_FUZZ_LOG("%s(%d,%d), drop payload_type %u expected %u\n", __func__, s->idx,
113+
s_port, payload_type, ops->payload_type);
114+
#endif
103115
dbg("%s(%d,%d), get payload_type %u but expect %u\n", __func__, s->idx, s_port,
104116
payload_type, ops->payload_type);
105117
ST_SESSION_STAT_INC(s, port_user_stats.common, stat_pkts_wrong_pt_dropped);
@@ -108,6 +120,10 @@ static int rx_ancillary_session_handle_pkt(struct mtl_main_impl* impl,
108120
if (ops->ssrc) {
109121
uint32_t ssrc = ntohl(rtp->ssrc);
110122
if (ssrc != ops->ssrc) {
123+
#ifdef MTL_ENABLE_FUZZING_ST40
124+
ST40_FUZZ_LOG("%s(%d,%d), drop ssrc %u expected %u\n", __func__, s->idx, s_port,
125+
ssrc, ops->ssrc);
126+
#endif
111127
dbg("%s(%d,%d), get ssrc %u but expect %u\n", __func__, s->idx, s_port, ssrc,
112128
ops->ssrc);
113129
ST_SESSION_STAT_INC(s, port_user_stats.common, stat_pkts_wrong_ssrc_dropped);
@@ -117,6 +133,10 @@ static int rx_ancillary_session_handle_pkt(struct mtl_main_impl* impl,
117133

118134
/* Drop if F is 0b01 (invalid: bit 0 set, bit 1 clear) */
119135
if ((rfc8331->first_hdr_chunk.f & 0x3) == 0x1) {
136+
#ifdef MTL_ENABLE_FUZZING_ST40
137+
ST40_FUZZ_LOG("%s(%d,%d), drop invalid field bits 0x%x\n", __func__, s->idx, s_port,
138+
rfc8331->first_hdr_chunk.f);
139+
#endif
120140
ST_SESSION_STAT_INC(s, port_user_stats, stat_pkts_wrong_interlace_dropped);
121141
return -EINVAL;
122142
}
@@ -149,9 +169,17 @@ static int rx_ancillary_session_handle_pkt(struct mtl_main_impl* impl,
149169
if ((mt_seq32_greater(s->tmstamp, tmstamp)) ||
150170
!mt_seq16_greater(seq_id, s->session_seq_id)) {
151171
if (!mt_seq16_greater(seq_id, s->session_seq_id)) {
172+
#ifdef MTL_ENABLE_FUZZING_ST40
173+
ST40_FUZZ_LOG("%s(%d,%d), redundant seq %u last %d\n", __func__, s->idx, s_port,
174+
seq_id, s->session_seq_id);
175+
#endif
152176
dbg("%s(%d,%d), redundant seq now %u session last %d\n", __func__, s->idx, s_port,
153177
seq_id, s->session_seq_id);
154178
} else {
179+
#ifdef MTL_ENABLE_FUZZING_ST40
180+
ST40_FUZZ_LOG("%s(%d,%d), redundant ts %u last %ld\n", __func__, s->idx, s_port,
181+
tmstamp, s->tmstamp);
182+
#endif
155183
dbg("%s(%d,%d), redundant tmstamp now %u session last %ld\n", __func__, s->idx,
156184
s_port, tmstamp, s->tmstamp);
157185
}
@@ -188,6 +216,10 @@ static int rx_ancillary_session_handle_pkt(struct mtl_main_impl* impl,
188216
if (ret < 0) {
189217
err("%s(%d), can not enqueue to the rte ring, packet drop, pkt seq %d\n", __func__,
190218
s->idx, seq_id);
219+
#ifdef MTL_ENABLE_FUZZING_ST40
220+
ST40_FUZZ_LOG("%s(%d,%d), enqueue failure for seq %u len %u\n", __func__, s->idx,
221+
s_port, seq_id, pkt_len);
222+
#endif
191223
ST_SESSION_STAT_INC(s, port_user_stats, stat_pkts_enqueue_fail);
192224
MT_USDT_ST40_RX_MBUF_ENQUEUE_FAIL(s->mgr->idx, s->idx, mbuf, tmstamp);
193225
return 0;
@@ -213,9 +245,51 @@ static int rx_ancillary_session_handle_pkt(struct mtl_main_impl* impl,
213245
}
214246

215247
MT_USDT_ST40_RX_MBUF_AVAILABLE(s->mgr->idx, s->idx, mbuf, tmstamp, pkt_len);
248+
#ifdef MTL_ENABLE_FUZZING_ST40
249+
info("%s(%d,%d), fuzz enqueued seq %u len %u\n", __func__, s->idx, s_port, seq_id,
250+
pkt_len);
251+
#endif
216252
return 0;
217253
}
218254

255+
#ifdef MTL_ENABLE_FUZZING_ST40
256+
int st_rx_ancillary_session_fuzz_handle_pkt(struct mtl_main_impl* impl,
257+
struct st_rx_ancillary_session_impl* s,
258+
struct rte_mbuf* mbuf,
259+
enum mtl_session_port s_port) {
260+
return rx_ancillary_session_handle_pkt(impl, s, mbuf, s_port);
261+
}
262+
263+
void st_rx_ancillary_session_fuzz_reset(struct st_rx_ancillary_session_impl* s) {
264+
if (!s) return;
265+
266+
s->session_seq_id = -1;
267+
s->tmstamp = -1;
268+
s->stat_pkts_dropped = 0;
269+
s->stat_pkts_redundant = 0;
270+
s->stat_pkts_out_of_order = 0;
271+
s->stat_pkts_enqueue_fail = 0;
272+
s->stat_pkts_wrong_pt_dropped = 0;
273+
s->stat_pkts_wrong_ssrc_dropped = 0;
274+
s->stat_pkts_received = 0;
275+
s->stat_last_time = 0;
276+
s->stat_max_notify_rtp_us = 0;
277+
s->stat_interlace_first_field = 0;
278+
s->stat_interlace_second_field = 0;
279+
s->stat_pkts_wrong_interlace_dropped = 0;
280+
rte_atomic32_set(&s->stat_frames_received, 0);
281+
mt_stat_u64_init(&s->stat_time);
282+
memset(&s->port_user_stats, 0, sizeof(s->port_user_stats));
283+
memset(s->stat_pkts_out_of_order_per_port, 0,
284+
sizeof(s->stat_pkts_out_of_order_per_port));
285+
286+
for (int i = 0; i < MTL_SESSION_PORT_MAX; i++) {
287+
s->latest_seq_id[i] = -1;
288+
s->redundant_error_cnt[i] = 0;
289+
}
290+
}
291+
#endif
292+
219293
static int rx_ancillary_session_handle_mbuf(void* priv, struct rte_mbuf** mbuf,
220294
uint16_t nb) {
221295
struct st_rx_session_priv* s_priv = priv;

lib/src/st2110/st_rx_ancillary_session.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@
1111

1212
#define ST_RX_ANCILLARY_PREFIX "RC_"
1313

14+
#ifdef MTL_ENABLE_FUZZING_ST40
15+
int st_rx_ancillary_session_fuzz_handle_pkt(struct mtl_main_impl* impl,
16+
struct st_rx_ancillary_session_impl* s,
17+
struct rte_mbuf* mbuf,
18+
enum mtl_session_port s_port);
19+
void st_rx_ancillary_session_fuzz_reset(struct st_rx_ancillary_session_impl* s);
20+
#endif
21+
1422
int st_rx_ancillary_sessions_sch_uinit(struct mtl_sch_impl* sch);
1523

1624
#endif

0 commit comments

Comments
 (0)