mirror of
https://github.com/mpv-player/mpv
synced 2024-12-19 13:21:13 +00:00
fuzzers: add new fuzzer targets
fuzzer_set_property.c: fuzz mpv_set_property in both initialized and non-initialized state. Useful for user provided values sanitization test. I've already seen some memory leaks in parsing code, good to drill it. fuzzer_loadfile.c: mpv_command "loadfile" test. Good for testing demuxers, decoding and playback loop. Sadly in headless mode we can't really test AO and VO, but at least all the code around can be fuzzed. Especially our custom demuxers like demux_mkv. fuzzer_loadfile_direct.c: Similar to loadfile above, but instead of saving the data to file, it passes the fuzz input in the command. Generated protocol specific versions (mf:// and memory:// for now) and generic one. Nothing really complex, but good start and even those few targets should give good coverage of the most common code paths in libmpv.
This commit is contained in:
parent
0b234af113
commit
47dbc3a74e
42
fuzzers/common.h
Normal file
42
fuzzers/common.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv 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.
|
||||
*
|
||||
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MPV_STRINGIFY_(X) #X
|
||||
#define MPV_STRINGIFY(X) MPV_STRINGIFY_(X)
|
||||
|
||||
static inline void check_error(int status)
|
||||
{
|
||||
if (status < 0) {
|
||||
fprintf(stderr, "mpv API error: %s\n", mpv_error_string(status));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool str_startswith(const char *str, size_t str_len,
|
||||
const char *prefix, size_t prefix_len)
|
||||
{
|
||||
if (str_len < prefix_len)
|
||||
return false;
|
||||
return !memcmp(str, prefix, prefix_len);
|
||||
}
|
71
fuzzers/fuzzer_loadfile.c
Normal file
71
fuzzers/fuzzer_loadfile.c
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv 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.
|
||||
*
|
||||
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libmpv/client.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return -1;
|
||||
|
||||
char filename[15 + 10 + 1];
|
||||
sprintf(filename, "/tmp/libfuzzer.%d", getpid());
|
||||
|
||||
FILE *fp = fopen(filename, "wb");
|
||||
if (!fp)
|
||||
exit(1);
|
||||
|
||||
if (fwrite(data, size, 1, fp) != 1)
|
||||
exit(1);
|
||||
|
||||
if (fclose(fp))
|
||||
exit(1);
|
||||
|
||||
mpv_handle *ctx = mpv_create();
|
||||
if (!ctx)
|
||||
exit(1);
|
||||
|
||||
check_error(mpv_set_option_string(ctx, "vo", "null"));
|
||||
check_error(mpv_set_option_string(ctx, "ao", "null"));
|
||||
check_error(mpv_set_option_string(ctx, "ao-null-untimed", "yes"));
|
||||
check_error(mpv_set_option_string(ctx, "untimed", "yes"));
|
||||
check_error(mpv_set_option_string(ctx, "video-osd", "no"));
|
||||
check_error(mpv_set_option_string(ctx, "msg-level", "all=trace"));
|
||||
|
||||
check_error(mpv_initialize(ctx));
|
||||
|
||||
const char *cmd[] = {"loadfile", filename, NULL};
|
||||
check_error(mpv_command(ctx, cmd));
|
||||
|
||||
while (1) {
|
||||
mpv_event *event = mpv_wait_event(ctx, 10000);
|
||||
if (event->event_id == MPV_EVENT_IDLE)
|
||||
break;
|
||||
}
|
||||
|
||||
mpv_terminate_destroy(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
77
fuzzers/fuzzer_loadfile_direct.c
Normal file
77
fuzzers/fuzzer_loadfile_direct.c
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv 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.
|
||||
*
|
||||
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libmpv/client.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
if (size <= 1 || data[size - 1] != '\0')
|
||||
return -1;
|
||||
|
||||
// Exlude data with null bytes inside
|
||||
if (strlen(data) != size - 1)
|
||||
return -1;
|
||||
|
||||
#ifdef MPV_PROTO
|
||||
if (!str_startswith(data, size - 1, MPV_STRINGIFY(MPV_PROTO) "://", strlen(MPV_STRINGIFY(MPV_PROTO) "://")))
|
||||
return -1;
|
||||
#else
|
||||
// Exclude some common paths that are not useful for testing.
|
||||
// Exclude -
|
||||
if (size == 2 && !strncmp(data, "-", 1))
|
||||
return -1;
|
||||
// Exclude relative paths
|
||||
if (str_startswith(data, size - 1, ".", 1))
|
||||
return -1;
|
||||
// Exclude absolute paths
|
||||
if (str_startswith(data, size - 1, "/", 1))
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
mpv_handle *ctx = mpv_create();
|
||||
if (!ctx)
|
||||
exit(1);
|
||||
|
||||
check_error(mpv_set_option_string(ctx, "vo", "null"));
|
||||
check_error(mpv_set_option_string(ctx, "ao", "null"));
|
||||
check_error(mpv_set_option_string(ctx, "ao-null-untimed", "yes"));
|
||||
check_error(mpv_set_option_string(ctx, "untimed", "yes"));
|
||||
check_error(mpv_set_option_string(ctx, "video-osd", "no"));
|
||||
check_error(mpv_set_option_string(ctx, "msg-level", "all=trace"));
|
||||
|
||||
check_error(mpv_initialize(ctx));
|
||||
|
||||
const char *cmd[] = {"loadfile", data, NULL};
|
||||
check_error(mpv_command(ctx, cmd));
|
||||
|
||||
while (1) {
|
||||
mpv_event *event = mpv_wait_event(ctx, 10000);
|
||||
if (event->event_id == MPV_EVENT_IDLE)
|
||||
break;
|
||||
}
|
||||
|
||||
mpv_terminate_destroy(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
89
fuzzers/fuzzer_set_property.c
Normal file
89
fuzzers/fuzzer_set_property.c
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* This file is part of mpv.
|
||||
*
|
||||
* mpv 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.
|
||||
*
|
||||
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libmpv/client.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
{
|
||||
size_t value_len;
|
||||
switch (MPV_FORMAT)
|
||||
{
|
||||
case MPV_FORMAT_STRING:
|
||||
value_len = strnlen(data, size);
|
||||
if (!value_len || value_len == size)
|
||||
return -1;
|
||||
value_len += 1;
|
||||
break;
|
||||
case MPV_FORMAT_FLAG:
|
||||
value_len = sizeof(int);
|
||||
break;
|
||||
case MPV_FORMAT_INT64:
|
||||
value_len = sizeof(int64_t);
|
||||
break;
|
||||
case MPV_FORMAT_DOUBLE:
|
||||
value_len = sizeof(double);
|
||||
break;
|
||||
default:
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
|
||||
// at least two bytes for the name
|
||||
if (size < value_len + 2)
|
||||
return -1;
|
||||
|
||||
const char *name = (const char *)data + value_len;
|
||||
size_t name_len = strnlen(name, size - value_len);
|
||||
if (!name_len || name_len != size - value_len - 1)
|
||||
return -1;
|
||||
|
||||
mpv_handle *ctx = mpv_create();
|
||||
if (!ctx)
|
||||
exit(1);
|
||||
|
||||
#if MPV_RUN
|
||||
check_error(mpv_set_option_string(ctx, "vo", "null"));
|
||||
check_error(mpv_set_option_string(ctx, "ao", "null"));
|
||||
check_error(mpv_set_option_string(ctx, "ao-null-untimed", "yes"));
|
||||
check_error(mpv_set_option_string(ctx, "untimed", "yes"));
|
||||
check_error(mpv_set_option_string(ctx, "video-osd", "no"));
|
||||
check_error(mpv_set_option_string(ctx, "msg-level", "all=trace"));
|
||||
|
||||
check_error(mpv_initialize(ctx));
|
||||
#endif
|
||||
|
||||
const void *value = data;
|
||||
mpv_set_property(ctx, name, MPV_FORMAT, &value);
|
||||
|
||||
#if MPV_RUN
|
||||
check_error(mpv_set_option_string(ctx, "audio-files", "av://lavfi:sine=d=0.1"));
|
||||
const char *cmd[] = {"loadfile", "av://lavfi:yuvtestsrc=d=0.1", NULL};
|
||||
check_error(mpv_command(ctx, cmd));
|
||||
|
||||
while (1) {
|
||||
mpv_event *event = mpv_wait_event(ctx, 10000);
|
||||
if (event->event_id == MPV_EVENT_IDLE)
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
mpv_terminate_destroy(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
26
fuzzers/meson.build
Normal file
26
fuzzers/meson.build
Normal file
@ -0,0 +1,26 @@
|
||||
incdir = include_directories('../')
|
||||
|
||||
executable('fuzzer_loadfile', 'fuzzer_loadfile.c',
|
||||
include_directories: incdir, link_with: libmpv)
|
||||
executable('fuzzer_loadfile_direct', 'fuzzer_loadfile_direct.c',
|
||||
include_directories: incdir, link_with: libmpv)
|
||||
|
||||
foreach p : ['bd', 'cdda', 'dvb', 'dvd', 'edl', 'file', 'hex', 'lavf', 'memory',
|
||||
'mf', 'slice', 'smb']
|
||||
executable('fuzzer_protocol_' + p,
|
||||
'fuzzer_loadfile_direct.c',
|
||||
c_args: ['-DMPV_PROTO=' + p],
|
||||
include_directories: incdir,
|
||||
link_with: libmpv)
|
||||
endforeach
|
||||
|
||||
|
||||
foreach f : ['MPV_FORMAT_STRING', 'MPV_FORMAT_FLAG', 'MPV_FORMAT_INT64', 'MPV_FORMAT_DOUBLE']
|
||||
foreach i : ['0', '1']
|
||||
executable('fuzzer_set_property_' + f + '_' + i,
|
||||
'fuzzer_set_property.c',
|
||||
c_args: ['-DMPV_FORMAT=' + f, '-DMPV_RUN=' + i],
|
||||
include_directories: incdir,
|
||||
link_with: libmpv)
|
||||
endforeach
|
||||
endforeach
|
13
meson.build
13
meson.build
@ -379,6 +379,15 @@ pthread_debug = get_option('pthread-debug').require(
|
||||
)
|
||||
features += {'pthread-debug': pthread_debug.allowed()}
|
||||
|
||||
if get_option('fuzzers')
|
||||
if get_option('cplayer') or not get_option('libmpv')
|
||||
error('fuzzers require !cplayer and libmpv')
|
||||
endif
|
||||
# Adding flags manually until https://github.com/mesonbuild/meson/pull/9825
|
||||
flags += ['-fsanitize=address,undefined,fuzzer', '-fno-omit-frame-pointer']
|
||||
link_flags += ['-fsanitize=address,undefined,fuzzer', '-fno-omit-frame-pointer']
|
||||
endif
|
||||
|
||||
add_project_arguments(flags, language: 'c')
|
||||
add_project_arguments(['-Wno-unused-parameter'], language: 'objc')
|
||||
add_project_link_arguments(link_flags, language: ['c', 'objc'])
|
||||
@ -1803,6 +1812,10 @@ if get_option('tests')
|
||||
subdir('test')
|
||||
endif
|
||||
|
||||
if get_option('fuzzers')
|
||||
subdir('fuzzers')
|
||||
endif
|
||||
|
||||
summary({'d3d11': features['d3d11'],
|
||||
'javascript': features['javascript'],
|
||||
'libmpv': get_option('libmpv'),
|
||||
|
@ -4,6 +4,7 @@ option('cplayer', type: 'boolean', value: true, description: 'mpv CLI player')
|
||||
option('libmpv', type: 'boolean', value: false, description: 'libmpv library')
|
||||
option('build-date', type: 'boolean', value: true, description: 'include compile timestamp in binary')
|
||||
option('tests', type: 'boolean', value: false, description: 'meson unit tests')
|
||||
option('fuzzers', type: 'boolean', value: false, description: 'fuzzer binaries')
|
||||
# Reminder: normally always built, but enabled by MPV_LEAK_REPORT.
|
||||
# Building it can be disabled only by defining NDEBUG through CFLAGS.
|
||||
option('ta-leak-report', type: 'boolean', value: false, description: 'enable ta leak report by default (development only)')
|
||||
|
Loading…
Reference in New Issue
Block a user