From e15a5753fd64c2e08b94ffc44ad376e23ffbe1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Denis-Courmont?= Date: Tue, 25 Oct 2016 17:01:35 +0300 Subject: [PATCH] vlc-demux-run: add demux fuzzing helper This includes support for statically linked plugins. It vastly increases the test iteration speed, which is critical for fuzz testing. Furthermore, it is necessary for coverage-driven fuzz testing to work at all. This also provides a (manually compiled only) back-end for LLVM's LibFuzzer using mostly the same code. 1) Debugging, regression testing or unguided fuzzing: - Make a normal build (debug and sanitization recommended). - Execute: "test/vlc-demux-run [demux name] " 2) American Fuzzy Lop run: - Make a *static* build with AFL as the toolchain. - (Where applicable) perform adequate religious luck granting offerings or other rites. - Run AFL with test/vlc-demux-run as the fuzzed executable. 3) LibFuzzer: - Make a preferrably static build with Clang as the toolchain. - Manually build test/vlc-demux-libfuzzer. - Run the executable with the LibFuzzer command line parameters syntax. --- test/Makefile.am | 69 +++++++ test/src/input/demux-run.c | 362 +++++++++++++++++++++++++++++++++++++ test/src/input/demux-run.h | 31 ++++ test/vlc-demux-libfuzzer.c | 41 +++++ test/vlc-demux-run.c | 52 ++++++ 5 files changed, 555 insertions(+) create mode 100644 test/src/input/demux-run.c create mode 100644 test/src/input/demux-run.h create mode 100644 test/vlc-demux-libfuzzer.c create mode 100644 test/vlc-demux-run.c diff --git a/test/Makefile.am b/test/Makefile.am index 95d4df6516..222edbde96 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -137,3 +137,72 @@ FORCE: @exit 1 .PHONY: FORCE + +libvlc_demux_run_la_SOURCES = src/input/demux-run.c src/input/demux-run.h +libvlc_demux_run_la_CPPFLAGS = $(AM_CPPFLAGS) \ + -DTOP_BUILDDIR=\"$$(cd "$(top_builddir)"; pwd)\" \ + -DTOP_SRCDIR=\"$$(cd "$(top_srcdir)"; pwd)\" +libvlc_demux_run_la_LDFLAGS = -no-install -static +libvlc_demux_run_la_LIBADD = \ + ../lib/libvlc.la ../src/libvlccore.la ../compat/libcompat.la +if !HAVE_DYNAMIC_PLUGINS +libvlc_demux_run_la_CPPFLAGS += -DHAVE_STATIC_MODULES +libvlc_demux_run_la_LIBADD += \ + ../modules/libaiff_plugin.la \ + ../modules/libasf_plugin.la \ + ../modules/libau_plugin.la \ + ../modules/libavi_plugin.la \ + ../modules/libcaf_plugin.la \ + ../modules/libdiracsys_plugin.la \ + ../modules/libes_plugin.la \ + ../modules/libflacsys_plugin.la \ + ../modules/libh26x_plugin.la \ + ../modules/libmjpeg_plugin.la \ + ../modules/libmkv_plugin.la \ + ../modules/libmp4_plugin.la \ + ../modules/libnsc_plugin.la \ + ../modules/libnsv_plugin.la \ + ../modules/libnuv_plugin.la \ + ../modules/libps_plugin.la \ + ../modules/libpva_plugin.la \ + ../modules/libsap_plugin.la \ + ../modules/libsmf_plugin.la \ + ../modules/libsubtitle_plugin.la \ + ../modules/libtta_plugin.la \ + ../modules/libttml_plugin.la \ + ../modules/libty_plugin.la \ + ../modules/libvoc_plugin.la \ + ../modules/libwav_plugin.la \ + ../modules/libxa_plugin.la \ + ../modules/libpacketizer_a52_plugin.la \ + ../modules/libpacketizer_dts_plugin.la \ + ../modules/libpacketizer_dirac_plugin.la \ + ../modules/libpacketizer_flac_plugin.la \ + ../modules/libpacketizer_h264_plugin.la \ + ../modules/libpacketizer_hevc_plugin.la \ + ../modules/libpacketizer_mlp_plugin.la \ + ../modules/libpacketizer_mpeg4audio_plugin.la \ + ../modules/libpacketizer_mpeg4video_plugin.la \ + ../modules/libpacketizer_mpegaudio_plugin.la \ + ../modules/libpacketizer_mpegvideo_plugin.la \ + ../modules/libpacketizer_vc1_plugin.la \ + ../modules/libfilesystem_plugin.la \ + ../modules/libxml_plugin.la \ + -lstdc++ +if HAVE_DVBPSI +libvlc_demux_run_la_CPPFLAGS += -DHAVE_DVBPSI +libvlc_demux_run_la_LIBADD += ../modules/libts_plugin.la +endif +endif +noinst_LTLIBRARIES = libvlc_demux_run.la + +# +# Fuzzers +# +vlc_demux_run_LDFLAGS = -no-install -static +vlc_demux_run_LDADD = libvlc_demux_run.la +noinst_PROGRAMS = vlc-demux-run + +vlc_demux_libfuzzer_CPPFLAGS = $(vlc_static_CPPFLAGS) +vlc_demux_libfuzzer_LDADD = -lFuzzer libvlc_demux_run.la +EXTRA_PROGRAMS += vlc-demux-libfuzzer diff --git a/test/src/input/demux-run.c b/test/src/input/demux-run.c new file mode 100644 index 0000000000..70f6cc3eec --- /dev/null +++ b/test/src/input/demux-run.c @@ -0,0 +1,362 @@ +/** + * @file demux-run.c + */ +/***************************************************************************** + * Copyright © 2016 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Rémi Denis-Courmont reserves the right to redistribute this file under + * the terms of the GNU Lesser General Public License as published by the + * the Free Software Foundation; either version 2.1 or the License, or + * (at his option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "../lib/libvlc_internal.h" + +#include + +#include "demux-run.h" + +#if 0 +#define debug(...) printf(__VA_ARGS__) +#else +#define debug(...) (void)0 +#endif + +struct test_es_out_t +{ + struct es_out_t out; + struct es_out_id_t *ids; +}; + +struct es_out_id_t +{ + struct es_out_id_t *next; +}; + +static es_out_id_t *EsOutAdd(es_out_t *out, const es_format_t *fmt) +{ + struct test_es_out_t *ctx = (struct test_es_out_t *) out; + + if (fmt->i_group < 0) + return NULL; + + es_out_id_t *id = malloc(sizeof (*id)); + if (unlikely(id == NULL)) + return NULL; + + id->next = ctx->ids; + ctx->ids = id; + + debug("[%p] Added ES\n", (void *)id); + return id; +} + +static void EsOutCheckId(es_out_t *out, es_out_id_t *id) +{ + struct test_es_out_t *ctx = (struct test_es_out_t *) out; + + for (es_out_id_t *ids = ctx->ids; ids != NULL; ids = ids->next) + if (ids == id) + return; + + abort(); +} + +static int EsOutSend(es_out_t *out, es_out_id_t *id, block_t *block) +{ + //debug("[%p] Sent ES: %zu\n", (void *)idd, block->i_buffer); + EsOutCheckId(out, id); + block_Release(block); + return VLC_SUCCESS; +} + +static void EsOutDelete(es_out_t *out, es_out_id_t *id) +{ + struct test_es_out_t *ctx = (struct test_es_out_t *) out; + es_out_id_t **pp = &ctx->ids; + + while (*pp != id) + { + if (*pp == NULL) + abort(); + pp = &((*pp)->next); + } + + debug("[%p] Deleted ES\n", (void *)id); + *pp = id->next; + free(id); +} + +static int EsOutControl(es_out_t *out, int query, va_list args) +{ + switch (query) + { + case ES_OUT_SET_ES: + break; + case ES_OUT_RESTART_ES: + abort(); + case ES_OUT_SET_ES_DEFAULT: + case ES_OUT_SET_ES_STATE: + break; + case ES_OUT_GET_ES_STATE: + EsOutCheckId(out, va_arg(args, es_out_id_t *)); + *va_arg(args, bool *) = true; + break; + case ES_OUT_SET_ES_CAT_POLICY: + break; + case ES_OUT_SET_GROUP: + abort(); + case ES_OUT_SET_PCR: + case ES_OUT_SET_GROUP_PCR: + case ES_OUT_RESET_PCR: + case ES_OUT_SET_ES_FMT: + case ES_OUT_SET_NEXT_DISPLAY_TIME: + case ES_OUT_SET_GROUP_META: + case ES_OUT_SET_GROUP_EPG: + case ES_OUT_DEL_GROUP: + case ES_OUT_SET_ES_SCRAMBLED_STATE: + break; + case ES_OUT_GET_EMPTY: + *va_arg(args, bool *) = true; + break; + case ES_OUT_SET_META: + break; + case ES_OUT_GET_PCR_SYSTEM: + case ES_OUT_MODIFY_PCR_SYSTEM: + abort(); + default: + return VLC_EGENERIC; + } + return VLC_SUCCESS; +} + +static void EsOutDestroy(es_out_t *out) +{ + struct test_es_out_t *ctx = (struct test_es_out_t *)out; + es_out_id_t *id; + + while ((id = ctx->ids) != NULL) + { + ctx->ids = id->next; + free(id); + } + free(ctx); +} + +static es_out_t *test_es_out_create(vlc_object_t *parent) +{ + struct test_es_out_t *ctx = malloc(sizeof (*ctx)); + if (ctx == NULL) + { + fprintf(stderr, "Error: cannot create ES output.\n"); + return NULL; + } + + ctx->ids = NULL; + + es_out_t *out = &ctx->out; + out->pf_add = EsOutAdd; + out->pf_send = EsOutSend; + out->pf_del = EsOutDelete; + out->pf_control = EsOutControl; + out->pf_destroy = EsOutDestroy; + out->p_sys = (void *)parent; + + return out; +} + +static int demux_process_stream(const char *name, stream_t *s) +{ + if (name == NULL) + name = "any"; + + if (s == NULL) + return -1; + + es_out_t *out = test_es_out_create(VLC_OBJECT(s)); + if (out == NULL) + return -1; + + demux_t *demux = demux_New(VLC_OBJECT(s), name, "", s, out); + if (demux == NULL) + { + es_out_Delete(out); + vlc_stream_Delete(s); + debug("Error: cannot create demultiplexer: %s\n", name); + return -1; + } + + uintmax_t i = 0; + int val; + + while ((val = demux_Demux(demux)) == VLC_DEMUXER_SUCCESS) + i++; + + demux_Delete(demux); + es_out_Delete(out); + + debug("Completed with %ju iteration(s).\n", i); + + return val == VLC_DEMUXER_EOF ? 0 : -1; +} + +static libvlc_instance_t *libvlc_create(void) +{ + const char *argv[] = { + NULL + }; + unsigned argc = (sizeof (argv) / sizeof (*argv)) - 1; + +#ifdef TOP_BUILDDIR +# ifndef HAVE_STATIC_MODULES + setenv("VLC_PLUGIN_PATH", TOP_BUILDDIR"/modules", 1); +# endif + setenv("VLC_DATA_PATH", TOP_SRCDIR"/share", 1); +#endif + + libvlc_instance_t *vlc = libvlc_new(argc, argv); + if (vlc == NULL) + fprintf(stderr, "Error: cannot initialize LibVLC.\n"); + return vlc; +} + +int vlc_demux_process_url(const char *demux, const char *url) +{ + libvlc_instance_t *vlc = libvlc_create(); + if (vlc == NULL) + return -1; + + stream_t *s = vlc_access_NewMRL(VLC_OBJECT(vlc->p_libvlc_int), url); + if (s == NULL) + fprintf(stderr, "Error: cannot create input stream: %s\n", url); + + int ret = demux_process_stream(demux, s); + libvlc_release(vlc); + return ret; +} + +int vlc_demux_process_path(const char *demux, const char *path) +{ + char *url = vlc_path2uri(path, NULL); + if (url == NULL) + { + fprintf(stderr, "Error: cannot convert path to URL: %s\n", path); + return -1; + } + + int ret = vlc_demux_process_url(demux, url); + free(url); + return ret; +} + +int vlc_demux_process_memory(const char *demux, + const unsigned char *buf, size_t length) +{ + libvlc_instance_t *vlc = libvlc_create(); + if (vlc == NULL) + return -1; + + stream_t *s = vlc_stream_MemoryNew(VLC_OBJECT(vlc->p_libvlc_int), + (void *)buf, length, true); + if (s == NULL) + fprintf(stderr, "Error: cannot create input stream\n"); + + int ret = demux_process_stream(demux, s); + libvlc_release(vlc); + return ret; +} + +#ifdef HAVE_STATIC_MODULES +# include + +typedef int (*vlc_plugin_cb)(int (*)(void *, void *, int, ...), void *); +extern vlc_plugin_cb vlc_static_modules[]; + +#define PLUGINS(f) \ + f(filesystem) \ + f(xml) \ + f(aiff) \ + f(asf) \ + f(au) \ + f(avi) \ + f(caf) \ + f(diracsys) \ + f(es) \ + f(flacsys) \ + f(h26x) \ + f(mjpeg) \ + f(mkv) \ + f(mp4) \ + f(nsc) \ + f(nsv) \ + f(ps) \ + f(pva) \ + f(sap) \ + f(smf) \ + f(subtitle) \ + PLUGIN_TS(f) \ + f(tta) \ + f(ttml) \ + f(ty) \ + f(voc) \ + f(wav) \ + f(xa) \ + f(a52) \ + f(dirac) \ + f(dts) \ + f(flac) \ + f(h264) \ + f(hevc) \ + f(mlp) \ + f(mpeg4audio) \ + f(mpeg4video) \ + f(mpegaudio) \ + f(mpegvideo) \ + f(vc1) +#ifdef HAVE_DVBPSI +# define PLUGIN_TS(f) f(ts) +#else +# define PLUGIN_TS(f) +#endif + +#define DECL_PLUGIN(p) \ + int vlc_entry__##p(int (*)(void *, void *, int, ...), void *); + +#define FUNC_PLUGIN(p) \ + vlc_entry__##p, + +PLUGINS(DECL_PLUGIN) + +__attribute__((visibility("default"))) +vlc_plugin_cb vlc_static_modules[] = { PLUGINS(FUNC_PLUGIN) NULL }; +#endif /* HAVE_STATIC_MODULES */ diff --git a/test/src/input/demux-run.h b/test/src/input/demux-run.h new file mode 100644 index 0000000000..e4c10f4f07 --- /dev/null +++ b/test/src/input/demux-run.h @@ -0,0 +1,31 @@ +/** + * @file demux-run.c + */ +/***************************************************************************** + * Copyright © 2016 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Rémi Denis-Courmont reserves the right to redistribute this file under + * the terms of the GNU Lesser General Public License as published by the + * the Free Software Foundation; either version 2.1 or the License, or + * (at his option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + + +int vlc_demux_process_url(const char *demux, const char *url); +int vlc_demux_process_path(const char *demux, const char *path); +int vlc_demux_process_memory(const char *demux, + const unsigned char *buf, size_t length); diff --git a/test/vlc-demux-libfuzzer.c b/test/vlc-demux-libfuzzer.c new file mode 100644 index 0000000000..b9774ebad2 --- /dev/null +++ b/test/vlc-demux-libfuzzer.c @@ -0,0 +1,41 @@ +/** + * @file vlc-demux-test.c + */ +/***************************************************************************** + * Copyright © 2016 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Rémi Denis-Courmont reserves the right to redistribute this file under + * the terms of the GNU Lesser General Public License as published by the + * the Free Software Foundation; either version 2.1 or the License, or + * (at his option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include "src/input/demux-run.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + vlc_demux_process_memory(getenv("VLC_DEMUX"), data, size); + return 0; +} diff --git a/test/vlc-demux-run.c b/test/vlc-demux-run.c new file mode 100644 index 0000000000..334dbc618f --- /dev/null +++ b/test/vlc-demux-run.c @@ -0,0 +1,52 @@ +/** + * @file vlc-demux-test.c + */ +/***************************************************************************** + * Copyright © 2016 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Rémi Denis-Courmont reserves the right to redistribute this file under + * the terms of the GNU Lesser General Public License as published by the + * the Free Software Foundation; either version 2.1 or the License, or + * (at his option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "src/input/demux-run.h" + +int main(int argc, char *argv[]) +{ + const char *demux = NULL, *filename; + + switch (argc) + { + case 3: + demux = argv[argc - 2]; + /* fall through */ + case 2: + filename = argv[argc - 1]; + break; + default: + fprintf(stderr, "Usage: %s [demux] \n", argv[0]); + return 1; + } + + return -vlc_demux_process_path(demux, filename); +} -- GitLab