123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- // Copyright 2016 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include <stddef.h>
- #include <stdint.h>
- #include <vector>
- #include "base/at_exit.h"
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/command_line.h"
- #include "base/location.h"
- #include "base/logging.h"
- #include "base/test/test_timeouts.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "media/base/bind_to_current_loop.h"
- #include "media/base/eme_constants.h"
- #include "media/base/media.h"
- #include "media/base/media_switches.h"
- #include "media/base/pipeline_status.h"
- #include "media/media_buildflags.h"
- #include "media/test/pipeline_integration_test_base.h"
- #include "media/test/test_media_source.h"
- #include "third_party/googletest/src/googletest/src/gtest-internal-inl.h"
- namespace {
- // Keep these aligned with BUILD.gn's pipeline_integration_fuzzer_variants
- enum FuzzerVariant {
- SRC,
- WEBM_OPUS,
- WEBM_VORBIS,
- WEBM_VP8,
- WEBM_VP9,
- WEBM_OPUS_VP9,
- #if BUILDFLAG(ENABLE_AV1_DECODER)
- MP4_AV1,
- #endif
- MP4_FLAC,
- MP4_OPUS,
- MP3,
- #if BUILDFLAG(USE_PROPRIETARY_CODECS)
- ADTS,
- MP4_AACLC,
- MP4_AACSBR,
- MP4_AVC1,
- MP4_AACLC_AVC,
- #if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
- MP2T_AACLC,
- MP2T_AACSBR,
- MP2T_AVC,
- MP2T_MP3,
- MP2T_AACLC_AVC,
- #endif // BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
- #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
- };
- std::string MseFuzzerVariantEnumToMimeTypeString(FuzzerVariant variant) {
- switch (variant) {
- case WEBM_OPUS:
- return "audio/webm; codecs=\"opus\"";
- case WEBM_VORBIS:
- return "audio/webm; codecs=\"vorbis\"";
- case WEBM_VP8:
- return "video/webm; codecs=\"vp8\"";
- case WEBM_VP9:
- return "video/webm; codecs=\"vp9\"";
- case WEBM_OPUS_VP9:
- return "video/webm; codecs=\"opus,vp9\"";
- #if BUILDFLAG(ENABLE_AV1_DECODER)
- case MP4_AV1:
- return "video/mp4; codecs=\"av01.0.04M.08\"";
- #endif // BUILDFLAG(ENABLE_AV1_DECODER)
- case MP4_FLAC:
- return "audio/mp4; codecs=\"flac\"";
- case MP4_OPUS:
- return "audio/mp4; codecs=\"opus\"";
- case MP3:
- return "audio/mpeg";
- #if BUILDFLAG(USE_PROPRIETARY_CODECS)
- case ADTS:
- return "audio/aac";
- case MP4_AACLC:
- return "audio/mp4; codecs=\"mp4a.40.2\"";
- case MP4_AACSBR:
- return "audio/mp4; codecs=\"mp4a.40.5\"";
- case MP4_AVC1:
- return "video/mp4; codecs=\"avc1.42E01E\"";
- case MP4_AACLC_AVC:
- return "video/mp4; codecs=\"mp4a.40.2,avc1.42E01E\"";
- #if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
- case MP2T_AACLC:
- return "video/mp2t; codecs=\"mp4a.67\"";
- case MP2T_AACSBR:
- return "video/mp2t; codecs=\"mp4a.40.5\"";
- case MP2T_AVC:
- return "video/mp2t; codecs=\"avc1.42E01E\"";
- case MP2T_MP3:
- // Note, "mp4a.6B" appears to be an equivalent codec substring.
- return "video/mp2t; codecs=\"mp4a.69\"";
- case MP2T_AACLC_AVC:
- return "video/mp2t; codecs=\"mp4a.40.2,avc1.42E01E\"";
- #endif // BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
- #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
- case SRC:
- NOTREACHED() << "SRC is an invalid MSE fuzzer variant";
- break;
- }
- return "";
- }
- } // namespace
- namespace media {
- // Limit the amount of initial (or post-seek) audio silence padding allowed in
- // rendering of fuzzed input.
- constexpr base::TimeDelta kMaxPlayDelay = base::Seconds(10);
- void OnEncryptedMediaInitData(media::PipelineIntegrationTestBase* test,
- media::EmeInitDataType /* type */,
- const std::vector<uint8_t>& /* init_data */) {
- // Encrypted media is not supported in this test. For an encrypted media file,
- // we will start demuxing the data but media pipeline will wait for a CDM to
- // be available to start initialization, which will not happen in this case.
- // To prevent the test timeout, we'll just fail the test immediately here.
- // Note: Since the callback is on the media task runner but the test is on
- // the main task runner, this must be posted.
- // TODO(xhwang): Support encrypted media in this fuzzer test.
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&PipelineIntegrationTestBase::FailTest,
- base::Unretained(test),
- media::PIPELINE_ERROR_INITIALIZATION_FAILED));
- }
- void OnAudioPlayDelay(media::PipelineIntegrationTestBase* test,
- base::TimeDelta play_delay) {
- CHECK_GT(play_delay, base::TimeDelta());
- if (play_delay > kMaxPlayDelay) {
- // Note: Since the callback is on the media task runner but the test is on
- // the main task runner, this must be posted.
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&PipelineIntegrationTestBase::FailTest,
- base::Unretained(test),
- media::PIPELINE_ERROR_INITIALIZATION_FAILED));
- }
- }
- class ProgressivePipelineIntegrationFuzzerTest
- : public PipelineIntegrationTestBase {
- public:
- ProgressivePipelineIntegrationFuzzerTest() {
- set_encrypted_media_init_data_cb(
- base::BindRepeating(&OnEncryptedMediaInitData, this));
- set_audio_play_delay_cb(
- BindToCurrentLoop(base::BindRepeating(&OnAudioPlayDelay, this)));
- }
- ~ProgressivePipelineIntegrationFuzzerTest() override = default;
- void RunTest(const uint8_t* data, size_t size) {
- if (PIPELINE_OK != Start(data, size, kUnreliableDuration | kFuzzing))
- return;
- Play();
- if (PIPELINE_OK != WaitUntilEndedOrError())
- return;
- Seek(base::TimeDelta());
- }
- };
- class MediaSourcePipelineIntegrationFuzzerTest
- : public PipelineIntegrationTestBase {
- public:
- MediaSourcePipelineIntegrationFuzzerTest() {
- set_encrypted_media_init_data_cb(
- base::BindRepeating(&OnEncryptedMediaInitData, this));
- set_audio_play_delay_cb(
- BindToCurrentLoop(base::BindRepeating(&OnAudioPlayDelay, this)));
- }
- ~MediaSourcePipelineIntegrationFuzzerTest() override = default;
- void RunTest(const uint8_t* data, size_t size, const std::string& mimetype) {
- if (size == 0)
- return;
- scoped_refptr<media::DecoderBuffer> buffer(
- DecoderBuffer::CopyFrom(data, size));
- TestMediaSource source(buffer, mimetype, kAppendWholeFile);
- // Prevent timeout in the case of not enough media appended to complete
- // demuxer initialization, yet no error in the media appended. The
- // following will trigger DEMUXER_ERROR_COULD_NOT_OPEN state transition in
- // this case.
- source.set_do_eos_after_next_append(true);
- source.set_encrypted_media_init_data_cb(
- base::BindRepeating(&OnEncryptedMediaInitData, this));
- // Allow parsing to either pass or fail without emitting a gtest failure
- // from TestMediaSource.
- source.set_expected_append_result(
- TestMediaSource::ExpectedAppendResult::kSuccessOrFailure);
- // TODO(wolenetz): Vary the behavior (abort/remove/seek/endOfStream/Append
- // in pieces/append near play-head/vary append mode/etc), perhaps using
- // CustomMutator and Seed to insert/update the variation information into/in
- // the |data| we process here. See https://crbug.com/750818.
- // Use |kFuzzing| test type to allow pipeline start to either pass or fail
- // without emitting a gtest failure.
- if (PIPELINE_OK != StartPipelineWithMediaSource(&source, kFuzzing, nullptr))
- return;
- Play();
- }
- };
- } // namespace media
- // Disable noisy logging.
- struct Environment {
- Environment() {
- base::CommandLine::Init(0, nullptr);
- // |test| instances uses TaskEnvironment, which needs TestTimeouts.
- TestTimeouts::Initialize();
- media::InitializeMediaLibrary();
- // Note, instead of LOG_FATAL, use a value at or below logging::LOG_VERBOSE
- // here to assist local debugging.
- logging::SetMinLogLevel(logging::LOG_FATAL);
- }
- };
- Environment* env = new Environment();
- // Entry point for LibFuzzer.
- extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- // Media pipeline starts new threads, which needs AtExitManager.
- base::AtExitManager at_exit;
- FuzzerVariant variant = PIPELINE_FUZZER_VARIANT;
- // These tests use GoogleTest assertions without using the GoogleTest
- // framework. While this is the case, tell GoogleTest's stack trace getter
- // that GoogleTest is being left now so that there is a basis for traces
- // collected upon assertion failure. TODO(https://crbug.com/1039559): use
- // RUN_ALL_TESTS() and remove this code.
- ::testing::internal::GetUnitTestImpl()
- ->os_stack_trace_getter()
- ->UponLeavingGTest();
- if (variant == SRC) {
- media::ProgressivePipelineIntegrationFuzzerTest test;
- test.RunTest(data, size);
- } else {
- media::MediaSourcePipelineIntegrationFuzzerTest test;
- test.RunTest(data, size, MseFuzzerVariantEnumToMimeTypeString(variant));
- }
- return 0;
- }
|