// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Fuzzer for media::cast remoting proto conversion functions.
// Exercises ConvertProtoToPipelineStatistics with attacker-controlled protobuf
// data. Targets unchecked static_cast of attacker-controlled int64 to
// AudioDecoderType/VideoDecoderType enums, which produces undefined behavior
// when out-of-range values are passed to GetDecoderName() (switch with no
// default compiled as jump table → OOB read → potential ASan crash).

#include <stddef.h>
#include <stdint.h>

#include <cstdio>
#include <sstream>
#include <tuple>

#include "base/containers/span.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/decoder.h"
#include "media/base/pipeline_status.h"
#include "media/base/video_decoder_config.h"
#include "media/cast/openscreen/remoting_proto_enum_utils.h"
#include "media/cast/openscreen/remoting_proto_utils.h"
#include "third_party/openscreen/src/cast/streaming/remoting.pb.h"

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  if (size == 0) {
    return 0;
  }

  // Use first byte as selector to choose which path to exercise.
  uint8_t selector = data[0];
  const uint8_t* payload = data + 1;
  size_t payload_size = size - 1;

  switch (selector % 5) {
    case 0: {
      // Test ByteArrayToDecoderBuffer with raw byte input.
      std::ignore = media::cast::ByteArrayToDecoderBuffer(
          // SAFETY: data is validated by the fuzzer runtime.
          UNSAFE_BUFFERS(base::span<const uint8_t>(payload, payload_size)));
      break;
    }

    case 1: {
      // Test ConvertProtoToAudioDecoderConfig.
      openscreen::cast::AudioDecoderConfig audio_message;
      if (audio_message.ParseFromArray(payload, payload_size)) {
        media::AudioDecoderConfig audio_config;
        std::ignore = media::cast::ConvertProtoToAudioDecoderConfig(
            audio_message, &audio_config);
      }
      break;
    }

    case 2: {
      // Test ConvertProtoToVideoDecoderConfig.
      openscreen::cast::VideoDecoderConfig video_message;
      if (video_message.ParseFromArray(payload, payload_size)) {
        media::VideoDecoderConfig video_config;
        std::ignore = media::cast::ConvertProtoToVideoDecoderConfig(
            video_message, &video_config);
      }
      break;
    }

    case 3: {
      // Test ConvertProtoToPipelineStatistics with out-of-range decoder_type.
      // The function does static_cast<media::AudioDecoderType>(int64_value)
      // and static_cast<media::VideoDecoderType>(int64_value) WITHOUT
      // bounds checking. When GetDecoderName() is called on the result,
      // the switch (with no default case) compiles to a jump table,
      // and out-of-range values cause OOB read → UB → potential ASan crash.
      openscreen::cast::PipelineStatistics stats_message;
      if (stats_message.ParseFromArray(payload, payload_size)) {
        media::PipelineStatistics stats;
        media::cast::ConvertProtoToPipelineStatistics(stats_message, &stats);

        // Trigger the UB by calling GetDecoderName on the tainted enum.
        // GetDecoderName has a switch with no default case - if the enum
        // value is out of range, the compiler-generated jump table
        // will be indexed OOB.
        const char* audio_name =
            media::GetDecoderName(stats.audio_pipeline_info.decoder_type);
        const char* video_name =
            media::GetDecoderName(stats.video_pipeline_info.decoder_type);

        // Force the compiler to not optimize away the calls.
        if (audio_name) {
          volatile char c = audio_name[0];
          (void)c;
        }
        if (video_name) {
          volatile char c = video_name[0];
          (void)c;
        }

        // Also exercise the ostream operator which calls GetDecoderName.
        std::ostringstream oss;
        oss << stats.audio_pipeline_info.decoder_type;
        oss << stats.video_pipeline_info.decoder_type;
      }
      break;
    }

    case 4: {
      // Test audio/video enum conversion functions directly.
      if (payload_size >= 3) {
        auto codec_val =
            static_cast<openscreen::cast::AudioDecoderConfig::Codec>(
                payload[0]);
        auto sample_val =
            static_cast<openscreen::cast::AudioDecoderConfig::SampleFormat>(
                payload[1]);
        auto layout_val =
            static_cast<openscreen::cast::AudioDecoderConfig::ChannelLayout>(
                payload[2]);
        std::ignore = media::cast::ToMediaAudioCodec(codec_val);
        std::ignore = media::cast::ToMediaSampleFormat(sample_val);
        std::ignore = media::cast::ToMediaChannelLayout(layout_val);
      }
      break;
    }
  }

  return 0;
}

