* [FFmpeg-devel] [PATCH] tools: add new fuzzers for avutil, avfilter, mux, network and cli
@ 2025-12-19 0:11 LP via ffmpeg-devel
0 siblings, 0 replies; only message in thread
From: LP via ffmpeg-devel @ 2025-12-19 0:11 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: LP
[-- Attachment #1: Type: text/plain, Size: 515 bytes --]
Hello FFmpeg developers,
This patch adds several new fuzzer targets to the tools/ directory to
improve
test coverage for various components, including libavutil (AVOptions),
libavfilter,
muxers, network protocols, and the CLI options parsing.
It also introduces tools/run_fuzzer_loop.c, a simple harness that allows
running
these fuzzers in a loop without requiring a full libFuzzer environment.
I have verified that the new targets compile and run successfully.
Please find the patch attached.
Best,
Leslie
[-- Attachment #2: 0001-tools-add-new-fuzzers-for-avutil-avfilter-mux-networ.patch --]
[-- Type: text/x-patch, Size: 29055 bytes --]
From 497c3929ba8ff6299aa3dd214fb73a841587852f Mon Sep 17 00:00:00 2001
From: "Leslie P. Polzer" <polzer@fastmail.com>
Date: Thu, 18 Dec 2025 12:51:04 +0000
Subject: [PATCH] tools: add new fuzzers for avutil, avfilter, mux, network and
cli
This patch adds new fuzzer targets to the tools directory:
- target_avutil_fuzzer: targets libavutil and AVOptions
- target_avfilter_fuzzer: targets specific filters
- target_mux_fuzzer: targets specific muxers
- target_network_fuzzer: targets network protocols
- target_cli_fuzzer: targets the ffmpeg CLI (exposes ffmpeg_cleanup)
It also adds run_fuzzer_loop.c, a harness for running these fuzzers
in a loop without libFuzzer.
Signed-off-by: Leslie P. Polzer <polzer@fastmail.com>
---
fftools/ffmpeg.c | 2 +-
fftools/ffmpeg.h | 1 +
tools/Makefile | 18 +++
tools/run_fuzzer_loop.c | 77 ++++++++++++
tools/target_avfilter_fuzzer.c | 164 ++++++++++++++++++++++++++
tools/target_avutil_fuzzer.c | 82 +++++++++++++
tools/target_cli_fuzzer.c | 123 ++++++++++++++++++++
tools/target_mux_fuzzer.c | 164 ++++++++++++++++++++++++++
tools/target_network_fuzzer.c | 207 +++++++++++++++++++++++++++++++++
9 files changed, 837 insertions(+), 1 deletion(-)
create mode 100644 tools/run_fuzzer_loop.c
create mode 100644 tools/target_avfilter_fuzzer.c
create mode 100644 tools/target_avutil_fuzzer.c
create mode 100644 tools/target_cli_fuzzer.c
create mode 100644 tools/target_mux_fuzzer.c
create mode 100644 tools/target_network_fuzzer.c
diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c
index c2c85d46bd..0b664f1426 100644
--- a/fftools/ffmpeg.c
+++ b/fftools/ffmpeg.c
@@ -310,7 +310,7 @@ static int decode_interrupt_cb(void *ctx)
const AVIOInterruptCB int_cb = { decode_interrupt_cb, NULL };
-static void ffmpeg_cleanup(int ret)
+void ffmpeg_cleanup(int ret)
{
if ((print_graphs || print_graphs_file) && nb_output_files > 0)
print_filtergraphs(filtergraphs, nb_filtergraphs, input_files, nb_input_files, output_files, nb_output_files);
diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h
index 7720dd9c59..0932079d46 100644
--- a/fftools/ffmpeg.h
+++ b/fftools/ffmpeg.h
@@ -798,6 +798,7 @@ extern int recast_media;
extern FILE *vstats_file;
void term_init(void);
+void ffmpeg_cleanup(int ret);
void term_exit(void);
void show_usage(void);
diff --git a/tools/Makefile b/tools/Makefile
index 7ae6e3cb75..294fbda81d 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -11,6 +11,24 @@ tools/target_enc_%_fuzzer.o: tools/target_enc_fuzzer.c
tools/target_bsf_%_fuzzer.o: tools/target_bsf_fuzzer.c
$(COMPILE_C) -DFFMPEG_BSF=$*
+tools/target_avfilter_%_fuzzer.o: tools/target_avfilter_fuzzer.c
+ $(COMPILE_C) -DFFMPEG_FILTER=$*
+
+tools/target_avutil_fuzzer.o: tools/target_avutil_fuzzer.c
+ $(COMPILE_C)
+
+tools/target_avutil_opt_fuzzer.o: tools/target_avutil_fuzzer.c
+ $(COMPILE_C) -DFFMPEG_AVUTIL_OPT
+
+tools/target_mux_%_fuzzer.o: tools/target_mux_fuzzer.c
+ $(COMPILE_C) -DFFMPEG_MUXER=$*
+
+tools/target_cli_fuzzer.o: tools/target_cli_fuzzer.c
+ $(COMPILE_C)
+
+tools/target_network_fuzzer.o: tools/target_network_fuzzer.c
+ $(COMPILE_C)
+
tools/target_dem_%_fuzzer.o: tools/target_dem_fuzzer.c
$(COMPILE_C) -DFFMPEG_DEMUXER=$* -DIO_FLAT=0
diff --git a/tools/run_fuzzer_loop.c b/tools/run_fuzzer_loop.c
new file mode 100644
index 0000000000..7c02572624
--- /dev/null
+++ b/tools/run_fuzzer_loop.c
@@ -0,0 +1,77 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <signal.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static volatile int stop = 0;
+
+void handle_alarm(int sig) {
+ stop = 1;
+}
+
+int main(int argc, char **argv) {
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <duration_seconds>\n", argv[0]);
+ return 1;
+ }
+
+ int duration = atoi(argv[1]);
+ signal(SIGALRM, handle_alarm);
+ alarm(duration);
+
+ uint8_t buf[65536];
+ size_t iterations = 0;
+ FILE *urandom = fopen("/dev/urandom", "rb");
+ if (!urandom) {
+ perror("fopen /dev/urandom");
+ return 1;
+ }
+
+ printf("Fuzzing for %d seconds...\n", duration);
+
+ while (!stop) {
+ // Generate random size between 1 and sizeof(buf)
+ size_t size = (rand() % sizeof(buf)) + 1;
+
+ // Read random data
+ if (fread(buf, 1, size, urandom) != size) {
+ break;
+ }
+
+ // Run fuzzer
+ LLVMFuzzerTestOneInput(buf, size);
+ iterations++;
+
+ if (iterations % 1000 == 0) {
+ printf("Iterations: %zu\r", iterations);
+ fflush(stdout);
+ }
+ }
+
+ fclose(urandom);
+ printf("\nFinished. Total iterations: %zu\n", iterations);
+ return 0;
+}
diff --git a/tools/target_avfilter_fuzzer.c b/tools/target_avfilter_fuzzer.c
new file mode 100644
index 0000000000..70f02d41be
--- /dev/null
+++ b/tools/target_avfilter_fuzzer.c
@@ -0,0 +1,164 @@
+/*
+ * Fuzzer for libavfilter
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libavutil/buffer.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/samplefmt.h"
+
+#include "libavfilter/avfilter.h"
+#include "libavfilter/buffersink.h"
+#include "libavfilter/buffersrc.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static void error(const char *err)
+{
+ fprintf(stderr, "%s", err);
+ exit(1);
+}
+
+// Global filter to fuzz. Initialize once if possible or via define.
+static const AVFilter *f = NULL;
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ int ret;
+ AVFilterGraph *graph = NULL;
+ AVFilterContext *filt_ctx = NULL, *src_ctx = NULL, *sink_ctx = NULL;
+ AVFrame *frame = NULL;
+ uint8_t *dummy_data = NULL;
+ const uint8_t *end = data + size;
+ char *options_str = NULL;
+ int options_len = 0;
+
+ // Ensure filter is set (compiled in)
+ if (!f) {
+#ifdef FFMPEG_FILTER
+#define XSTR(s) STR(s)
+#define STR(s) #s
+ f = avfilter_get_by_name(XSTR(FFMPEG_FILTER));
+#endif
+ if (!f) {
+ fprintf(stderr, "Filter not found: %s\n", XSTR(FFMPEG_FILTER));
+ return 0;
+ }
+ av_log_set_level(AV_LOG_PANIC);
+ }
+
+ graph = avfilter_graph_alloc();
+ if (!graph) error("Failed to allocate graph");
+
+ // We need at least some data for options and frame params
+ // Let's reserve up to 1024 bytes for options if available
+ if (size > 0) {
+ options_len = size > 512 ? 512 : size;
+ options_str = av_malloc(options_len + 1);
+ if (!options_str) error("Failed to allocate options");
+ memcpy(options_str, data, options_len);
+ options_str[options_len] = 0;
+ data += options_len;
+ size -= options_len;
+ }
+
+ // Determine filter type to setup source/sink
+ int is_audio = 0;
+ if (f->inputs && avfilter_pad_get_type(f->inputs, 0) == AVMEDIA_TYPE_AUDIO) is_audio = 1;
+ // Some filters might have no inputs (sources), handled separately?
+ // For now assume 1 input 1 output or similar standard filters.
+
+ const AVFilter *buffersrc = avfilter_get_by_name(is_audio ? "abuffer" : "buffer");
+ const AVFilter *buffersink = avfilter_get_by_name(is_audio ? "abuffersink" : "buffersink");
+
+ char src_args[256];
+ if (is_audio) {
+ snprintf(src_args, sizeof(src_args), "time_base=1/44100:sample_rate=44100:sample_fmt=flt:channel_layout=stereo");
+ } else {
+ snprintf(src_args, sizeof(src_args), "video_size=640x480:pix_fmt=yuv420p:time_base=1/25:pixel_aspect=1/1");
+ }
+
+ ret = avfilter_graph_create_filter(&src_ctx, buffersrc, "in", src_args, NULL, graph);
+ if (ret < 0) goto end;
+
+ ret = avfilter_graph_create_filter(&filt_ctx, f, "filter", options_str, NULL, graph);
+ if (ret < 0) goto end;
+
+ ret = avfilter_graph_create_filter(&sink_ctx, buffersink, "out", NULL, NULL, graph);
+ if (ret < 0) goto end;
+
+ // Link
+ if (src_ctx && filt_ctx) {
+ ret = avfilter_link(src_ctx, 0, filt_ctx, 0);
+ if (ret < 0) goto end;
+ }
+ if (filt_ctx && sink_ctx) {
+ ret = avfilter_link(filt_ctx, 0, sink_ctx, 0);
+ if (ret < 0) goto end;
+ }
+
+ ret = avfilter_graph_config(graph, NULL);
+ if (ret < 0) goto end;
+
+ // Send a frame
+ frame = av_frame_alloc();
+ if (!frame) error("Failed to allocate frame");
+
+ if (is_audio) {
+ frame->nb_samples = 1024;
+ frame->format = AV_SAMPLE_FMT_FLT;
+ av_channel_layout_default(&frame->ch_layout, 2);
+ frame->sample_rate = 44100;
+ } else {
+ frame->width = 640;
+ frame->height = 480;
+ frame->format = AV_PIX_FMT_YUV420P;
+ }
+
+ ret = av_frame_get_buffer(frame, 0);
+ if (ret < 0) goto end;
+
+ // Fill frame with fuzz data if available
+ if (size > 0) {
+ int copy_size = size;
+ // Naive fill, just copy into first plane
+ if (copy_size > frame->buf[0]->size) copy_size = frame->buf[0]->size;
+ memcpy(frame->data[0], data, copy_size);
+ }
+
+ ret = av_buffersrc_add_frame(src_ctx, frame);
+ if (ret < 0) goto end;
+
+ // Receive output
+ while (1) {
+ AVFrame *out = av_frame_alloc();
+ ret = av_buffersink_get_frame(sink_ctx, out);
+ av_frame_free(&out);
+ if (ret < 0) break;
+ }
+
+end:
+ if (options_str) av_free(options_str);
+ if (frame) av_frame_free(&frame);
+ if (graph) avfilter_graph_free(&graph);
+
+ return 0;
+}
diff --git a/tools/target_avutil_fuzzer.c b/tools/target_avutil_fuzzer.c
new file mode 100644
index 0000000000..4d621f4915
--- /dev/null
+++ b/tools/target_avutil_fuzzer.c
@@ -0,0 +1,82 @@
+/*
+ * Fuzzer for libavutil
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libavutil/avutil.h"
+#include "libavutil/eval.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+// Dummy class for option fuzzing
+typedef struct DummyContext {
+ const AVClass *class;
+ int int_val;
+ char *str_val;
+ double dbl_val;
+ int64_t i64_val;
+} DummyContext;
+
+static const AVOption dummy_options[] = {
+ { "int", "integer option", offsetof(DummyContext, int_val), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, 0 },
+ { "str", "string option", offsetof(DummyContext, str_val), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, 0 },
+ { "dbl", "double option", offsetof(DummyContext, dbl_val), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -100.0, 100.0, 0 },
+ { "i64", "int64 option", offsetof(DummyContext, i64_val), AV_OPT_TYPE_INT64, {.i64 = 0}, INT64_MIN, INT64_MAX, 0 },
+ { NULL }
+};
+
+static const AVClass dummy_class = {
+ .class_name = "Dummy",
+ .item_name = av_default_item_name,
+ .option = dummy_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ char *str;
+
+ // Limit size to avoid excessive processing time
+ if (size > 8192) size = 8192;
+
+ str = av_malloc(size + 1);
+ if (!str) return 0;
+
+ memcpy(str, data, size);
+ str[size] = 0;
+
+#ifdef FFMPEG_AVUTIL_OPT
+ DummyContext obj = { .class = &dummy_class };
+ av_opt_set_defaults(&obj);
+
+ // Fuzz option parsing (key=value:key2=value2...)
+ av_set_options_string(&obj, str, "=", ":");
+
+ av_opt_free(&obj);
+#else
+ // Default: av_expr
+ double res;
+ // Fuzz av_expr_parse_and_eval
+ av_expr_parse_and_eval(&res, str, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, NULL);
+#endif
+
+ av_free(str);
+ return 0;
+}
\ No newline at end of file
diff --git a/tools/target_cli_fuzzer.c b/tools/target_cli_fuzzer.c
new file mode 100644
index 0000000000..5cee732039
--- /dev/null
+++ b/tools/target_cli_fuzzer.c
@@ -0,0 +1,123 @@
+/*
+ * Fuzzer for ffmpeg CLI option parsing
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+
+#include "fftools/ffmpeg.h"
+#include "fftools/ffmpeg_sched.h"
+
+// Globals from ffmpeg.c that need resetting
+extern int nb_input_files;
+extern int nb_output_files;
+extern int nb_filtergraphs;
+extern int nb_decoders;
+
+// Extern function from ffmpeg_opt.c
+int ffmpeg_parse_options(int argc, char **argv, Scheduler *sch);
+
+static jmp_buf jmp_env;
+
+// Mock exit to return control to fuzzer
+void __wrap_exit(int status) {
+ longjmp(jmp_env, 1);
+}
+
+// Mock other functions that might be problematic or unwanted during fuzzing
+// For now, we assume others are safe or handled by ffmpeg_cleanup
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ int argc = 0;
+ char **argv = NULL;
+ char *input_copy = NULL;
+ Scheduler *sch = NULL;
+
+ // We need at least "ffmpeg" as argv[0]
+ // Parse data into argv. We treat null bytes or newlines as separators?
+ // Or just null bytes.
+ // Let's create a copy of data to be safe and mutable
+ input_copy = malloc(size + 1);
+ if (!input_copy) return 0;
+ memcpy(input_copy, data, size);
+ input_copy[size] = 0;
+
+ // Count args
+ argc = 1; // argv[0]
+ for (size_t i = 0; i < size; i++) {
+ if (input_copy[i] == 0 || input_copy[i] == '\n') {
+ argc++;
+ }
+ }
+
+ argv = malloc((argc + 1) * sizeof(char *));
+ if (!argv) {
+ free(input_copy);
+ return 0;
+ }
+
+ argv[0] = "ffmpeg";
+ int current_arg = 1;
+ char *ptr = input_copy;
+ char *end = input_copy + size;
+
+ // Simple tokenizer
+ char *token = ptr;
+ for (size_t i = 0; i <= size; i++) {
+ if (input_copy[i] == 0 || input_copy[i] == '\n') {
+ input_copy[i] = 0; // Ensure null termination
+ if (current_arg < argc) {
+ argv[current_arg++] = token;
+ }
+ token = &input_copy[i+1];
+ }
+ }
+ argv[argc] = NULL;
+
+ // Allocate scheduler
+ sch = sch_alloc();
+ if (!sch) goto end;
+
+ // Prepare to catch exit
+ if (setjmp(jmp_env) == 0) {
+ // Run option parsing
+ // This will likely fail and call exit() often with fuzz data
+ ffmpeg_parse_options(argc, argv, sch);
+ }
+
+ // Cleanup
+ ffmpeg_cleanup(0);
+
+ // Reset globals to allow next run
+ nb_input_files = 0;
+ nb_output_files = 0;
+ nb_filtergraphs = 0;
+ nb_decoders = 0;
+ // Also reset option globals if possible? uninit_opts called in cleanup
+
+end:
+ if (sch) sch_free(&sch);
+ free(argv);
+ free(input_copy);
+ return 0;
+}
diff --git a/tools/target_mux_fuzzer.c b/tools/target_mux_fuzzer.c
new file mode 100644
index 0000000000..4c0b904fe3
--- /dev/null
+++ b/tools/target_mux_fuzzer.c
@@ -0,0 +1,164 @@
+/*
+ * Fuzzer for libavformat muxers
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libavutil/avassert.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "libavutil/intreadwrite.h"
+#include "libavformat/avformat.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+static void error(const char *err)
+{
+ fprintf(stderr, "%s", err);
+ exit(1);
+}
+
+// Global muxer to fuzz.
+static const AVOutputFormat *fmt = NULL;
+
+// Mock IO to discard output
+static int write_packet(void *opaque, const uint8_t *buf, int buf_size)
+{
+ return buf_size;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ AVFormatContext *oc = NULL;
+ AVPacket *pkt = NULL;
+ AVIOContext *pb = NULL;
+ uint8_t *io_buffer = NULL;
+ int io_buffer_size = 32768;
+ int ret;
+ const uint8_t *end = data + size;
+
+ if (!fmt) {
+#ifdef FFMPEG_MUXER
+#define XSTR(s) STR(s)
+#define STR(s) #s
+ fmt = av_guess_format(XSTR(FFMPEG_MUXER), NULL, NULL);
+#endif
+ // Fallback to a common default if not specified
+ if (!fmt) fmt = av_guess_format("mp4", NULL, NULL);
+ if (!fmt) return 0;
+ av_log_set_level(AV_LOG_PANIC);
+ }
+
+ if (size < 16) return 0;
+
+ // Allocate IO buffer
+ io_buffer = av_malloc(io_buffer_size);
+ if (!io_buffer) return 0;
+
+ pb = avio_alloc_context(io_buffer, io_buffer_size, 1, NULL, NULL, write_packet, NULL);
+ if (!pb) {
+ av_free(io_buffer);
+ return 0;
+ }
+
+ ret = avformat_alloc_output_context2(&oc, fmt, NULL, NULL);
+ if (ret < 0 || !oc) {
+ avio_context_free(&pb);
+ return 0;
+ }
+ oc->pb = pb;
+
+ // Parse fuzz data to create streams
+ // First byte: number of streams (cap at 10)
+ int nb_streams = data[0] % 10;
+ if (nb_streams == 0) nb_streams = 1;
+ data++;
+ size--;
+
+ for (int i = 0; i < nb_streams; i++) {
+ AVStream *st = avformat_new_stream(oc, NULL);
+ if (!st) break;
+
+ // Randomize stream codec parameters
+ if (size < 4) break;
+ uint32_t codec_tag = AV_RL32(data);
+ data += 4; size -= 4;
+
+ st->codecpar->codec_type = (i % 2 == 0) ? AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO;
+ st->codecpar->codec_id = AV_CODEC_ID_H264; // Default to something common
+ st->codecpar->codec_tag = codec_tag;
+ st->time_base = (AVRational){1, 25};
+
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ st->codecpar->width = 640;
+ st->codecpar->height = 480;
+ } else {
+ st->codecpar->sample_rate = 44100;
+ av_channel_layout_default(&st->codecpar->ch_layout, 2);
+ }
+ }
+
+ // Write header
+ if (avformat_write_header(oc, NULL) < 0) {
+ goto end;
+ }
+
+ pkt = av_packet_alloc();
+ if (!pkt) goto end;
+
+ // Packet loop
+ while (size > 16) {
+ // [stream_idx 1b][pts 8b][dts 8b][flags 1b][size 2b][data ...]
+ uint8_t stream_idx = data[0];
+ int64_t pts = AV_RL64(data + 1);
+ int64_t dts = AV_RL64(data + 9);
+ int flags = data[17];
+ int payload_size = AV_RL16(data + 18);
+ data += 20; size -= 20;
+
+ if (oc->nb_streams == 0) break;
+ stream_idx %= oc->nb_streams;
+
+ if (payload_size > size) payload_size = size;
+
+ // Populate packet
+ av_new_packet(pkt, payload_size);
+ memcpy(pkt->data, data, payload_size);
+ pkt->stream_index = stream_idx;
+ pkt->pts = pts;
+ pkt->dts = dts;
+ pkt->flags = flags;
+
+ // Mux
+ av_interleaved_write_frame(oc, pkt);
+ av_packet_unref(pkt);
+
+ data += payload_size;
+ size -= payload_size;
+ }
+
+ av_write_trailer(oc);
+
+end:
+ av_packet_free(&pkt);
+ // free output context before IO because it might flush
+ if (oc) avformat_free_context(oc);
+ if (pb) avio_context_free(&pb);
+ // io_buffer is freed by avio_context_free
+
+ return 0;
+}
diff --git a/tools/target_network_fuzzer.c b/tools/target_network_fuzzer.c
new file mode 100644
index 0000000000..aaaa543b29
--- /dev/null
+++ b/tools/target_network_fuzzer.c
@@ -0,0 +1,207 @@
+/*
+ * Fuzzer for network protocols (mocking sockets)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libavformat/avformat.h"
+#include "libavformat/url.h"
+#include "libavutil/avassert.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <errno.h>
+
+// --- Mock State ---
+static const uint8_t *g_fuzz_data = NULL;
+static size_t g_fuzz_size = 0;
+static size_t g_fuzz_pos = 0;
+static int g_fake_fd = 42; // arbitrary number
+
+// --- Mock Functions ---
+
+int __wrap_socket(int domain, int type, int protocol) {
+ return g_fake_fd;
+}
+
+int __wrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
+ if (sockfd != g_fake_fd) return -1;
+ return 0; // Success
+}
+
+int __wrap_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
+ return 0;
+}
+
+int __wrap_listen(int sockfd, int backlog) {
+ return 0;
+}
+
+int __wrap_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
+ return -1; // Not fuzzing server side yet
+}
+
+ssize_t __wrap_recv(int sockfd, void *buf, size_t len, int flags) {
+ if (sockfd != g_fake_fd) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (g_fuzz_pos >= g_fuzz_size) {
+ // EOF
+ return 0;
+ }
+
+ size_t remaining = g_fuzz_size - g_fuzz_pos;
+ size_t to_read = len < remaining ? len : remaining;
+
+ if (to_read == 0) return 0;
+
+ memcpy(buf, g_fuzz_data + g_fuzz_pos, to_read);
+ g_fuzz_pos += to_read;
+ return to_read;
+}
+
+ssize_t __wrap_send(int sockfd, const void *buf, size_t len, int flags) {
+ if (sockfd != g_fake_fd) {
+ errno = EBADF;
+ return -1;
+ }
+ // Sink the data
+ return len;
+}
+
+int __wrap_shutdown(int sockfd, int how) {
+ return 0;
+}
+
+int __wrap_close(int fd) {
+ if (fd == g_fake_fd) return 0;
+ // Call real close? We can't easily without dlsym.
+ // Just assume it's fine or no-op.
+ return 0;
+}
+
+int __wrap_setsockopt(int sockfd, int level, int optname,
+ const void *optval, socklen_t optlen) {
+ return 0;
+}
+
+int __wrap_getsockopt(int sockfd, int level, int optname,
+ void *optval, socklen_t *optlen) {
+ // Fake buffer size to avoid errors
+ if (level == SOL_SOCKET && optname == SO_RCVBUF) {
+ int *val = (int*)optval;
+ *val = 32768;
+ return 0;
+ }
+ return 0;
+}
+
+int __wrap_fcntl(int fd, int cmd, ...) {
+ return 0; // Success
+}
+
+int __wrap_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
+ for (nfds_t i = 0; i < nfds; i++) {
+ if (fds[i].fd == g_fake_fd) {
+ fds[i].revents = 0;
+ if (fds[i].events & POLLIN) {
+ // Always say readable, so recv is called.
+ // recv will return 0 if empty/EOF.
+ fds[i].revents |= POLLIN;
+ }
+ if (fds[i].events & POLLOUT) {
+ fds[i].revents |= POLLOUT;
+ }
+ }
+ }
+ return nfds;
+}
+
+int __wrap_getaddrinfo(const char *node, const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res) {
+ // Return a fake address
+ struct addrinfo *ai = calloc(1, sizeof(struct addrinfo));
+ struct sockaddr_in *sa = calloc(1, sizeof(struct sockaddr_in));
+
+ sa->sin_family = AF_INET;
+ sa->sin_port = htons(80);
+ sa->sin_addr.s_addr = htonl(0x7F000001); // 127.0.0.1
+
+ ai->ai_family = AF_INET;
+ ai->ai_socktype = SOCK_STREAM;
+ ai->ai_protocol = IPPROTO_TCP;
+ ai->ai_addr = (struct sockaddr*)sa;
+ ai->ai_addrlen = sizeof(struct sockaddr_in);
+
+ *res = ai;
+ return 0;
+}
+
+void __wrap_freeaddrinfo(struct addrinfo *res) {
+ if (res) {
+ free(res->ai_addr);
+ free(res);
+ }
+}
+
+
+// --- Fuzzer Entry ---
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ AVFormatContext *fmt = NULL;
+ int ret;
+
+ // Setup fuzz data
+ g_fuzz_data = data;
+ g_fuzz_size = size;
+ g_fuzz_pos = 0;
+
+ // We target HTTP for now as it's the most common complex protocol.
+ // The input data will be "read" by the HTTP client as the server response.
+ // Note: HTTP client writes a request first. Our mock send sinks it.
+ // Then it reads response.
+
+ // We need to use a dummy URL that triggers TCP.
+ // "http://127.0.0.1/"
+
+ // To fuzz different protocols, we could look at the first byte of data?
+ // Or just hardcode for now.
+
+ ret = avformat_open_input(&fmt, "http://127.0.0.1/fuzz", NULL, NULL);
+ if (ret >= 0) {
+ // If opened successfully, read some packets
+ AVPacket *pkt = av_packet_alloc();
+ int i = 0;
+ while (i++ < 100 && av_read_frame(fmt, pkt) >= 0) {
+ av_packet_unref(pkt);
+ }
+ av_packet_free(&pkt);
+ avformat_close_input(&fmt);
+ }
+
+ return 0;
+}
--
2.43.0
[-- Attachment #3: Type: text/plain, Size: 163 bytes --]
_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2025-12-19 0:12 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-19 0:11 [FFmpeg-devel] [PATCH] tools: add new fuzzers for avutil, avfilter, mux, network and cli LP via ffmpeg-devel
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git