1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127

media / filters / vpx_video_decoder_fuzzertest.cc [blame]

// Copyright 2016 The Chromium Authors
// 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 <random>

#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media.h"
#include "media/base/media_util.h"
#include "media/filters/vpx_video_decoder.h"

struct Env {
  Env() {
    media::InitializeMediaLibrary();
    base::CommandLine::Init(0, nullptr);
    logging::SetMinLogLevel(logging::LOGGING_FATAL);
  }

  base::AtExitManager at_exit_manager;
  base::test::SingleThreadTaskEnvironment task_environment;
};

void OnDecodeComplete(base::OnceClosure quit_closure,
                      media::DecoderStatus status) {
  std::move(quit_closure).Run();
}

void OnInitDone(base::OnceClosure quit_closure,
                bool* success_dest,
                media::DecoderStatus status) {
  *success_dest = status.is_ok();
  std::move(quit_closure).Run();
}

void OnOutputComplete(scoped_refptr<media::VideoFrame> frame) {}

// Entry point for LibFuzzer.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data_ptr, size_t size) {
  // SAFETY: LibFuzzer must pass a valid `data_ptr` and `size`.
  auto data = UNSAFE_BUFFERS(base::span(data_ptr, size));

  // Create Env on the first run of LLVMFuzzerTestOneInput otherwise
  // message_loop will be created before this process forks when used with AFL,
  // causing hangs.
  [[maybe_unused]] static Env* env = new Env();
  std::mt19937_64 rng;

  {  // Seed rng from data.
    std::string str(base::as_string_view(data));
    std::size_t data_hash = std::hash<std::string>()(str);
    rng.seed(data_hash);
  }

  // Compute randomized constants. Put all rng() usages here.
  // Use only values that pass DCHECK in VpxVideoDecoder::ConfigureDecoder().
  media::VideoCodec codec;

  bool has_alpha = false;
  if (rng() & 1) {
    codec = media::VideoCodec::kVP8;
    // non-Alpha VP8 decoding isn't supported by VpxVideoDecoder on Linux.
    has_alpha = true;
  } else {
    codec = media::VideoCodec::kVP9;
    has_alpha = rng() & 1;
  }

  auto profile = static_cast<media::VideoCodecProfile>(
      rng() % media::VIDEO_CODEC_PROFILE_MAX);
  auto color_space =
      media::VideoColorSpace(rng() % 256, rng() % 256, rng() % 256,
                             (rng() & 1) ? gfx::ColorSpace::RangeID::LIMITED
                                         : gfx::ColorSpace::RangeID::FULL);
  auto rotation =
      static_cast<media::VideoRotation>(rng() % media::VIDEO_ROTATION_MAX);
  auto coded_size = gfx::Size(1 + (rng() % 127), 1 + (rng() % 127));
  auto visible_rect = gfx::Rect(coded_size);
  auto natural_size = gfx::Size(1 + (rng() % 127), 1 + (rng() % 127));
  uint8_t reflection = rng() % 4;

  media::VideoDecoderConfig config(
      codec, profile,
      has_alpha ? media::VideoDecoderConfig::AlphaMode::kHasAlpha
                : media::VideoDecoderConfig::AlphaMode::kIsOpaque,
      color_space, media::VideoTransformation(rotation, reflection), coded_size,
      visible_rect, natural_size, media::EmptyExtraData(),
      media::EncryptionScheme::kUnencrypted);

  if (!config.IsValidConfig())
    return 0;

  media::VpxVideoDecoder decoder;

  {
    base::RunLoop run_loop;
    bool success = false;
    decoder.Initialize(
        config, true /* low_delay */, nullptr /* cdm_context */,
        base::BindOnce(&OnInitDone, run_loop.QuitClosure(), &success),
        base::BindRepeating(&OnOutputComplete), base::NullCallback());
    run_loop.Run();
    if (!success)
      return 0;
  }

  {
    base::RunLoop run_loop;
    auto buffer = media::DecoderBuffer::CopyFrom(data);
    decoder.Decode(buffer,
                   base::BindOnce(&OnDecodeComplete, run_loop.QuitClosure()));
    run_loop.Run();
  }

  return 0;
}