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

// Takes an Input protobuf message from libprotobuf-mutator, converts it to an
// actual skia image filter and then applies it to a canvas for the purpose of
// fuzzing skia. This should uncover bugs that could be used by a compromised
// renderer to exploit the browser process.

#include <stdlib.h>

#include <iostream>
#include <string>

#include "testing/libfuzzer/proto/skia_image_filter_proto_converter.h"

#include "base/memory/raw_ptr.h"
#include "base/process/memory.h"
#include "base/test/test_discardable_memory_allocator.h"
#include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkImageFilter.h"

using skia_image_filter_proto_converter::Input;
using skia_image_filter_proto_converter::Converter;

static const int kBitmapSize = 24;

struct Environment {
  raw_ptr<base::TestDiscardableMemoryAllocator> discardable_memory_allocator;
  Environment() {
    base::EnableTerminationOnOutOfMemory();
    discardable_memory_allocator = new base::TestDiscardableMemoryAllocator();
    base::DiscardableMemoryAllocator::SetInstance(discardable_memory_allocator);
  }
};

DEFINE_PROTO_FUZZER(const Input& input) {
  [[maybe_unused]] static Environment environment = Environment();

  static Converter converter = Converter();
  std::string ipc_filter_message = converter.Convert(input);

  // Allow the flattened skia filter to be retrieved easily.
  if (getenv("LPM_DUMP_NATIVE_INPUT")) {
    // Don't write a newline since it will make the output invalid (so that it
    // cannot be fed to filter_fuzz_stub) Flush instead.
    std::cout << ipc_filter_message << std::flush;
  }

  sk_sp<SkImageFilter> flattenable = SkImageFilter::Deserialize(
      ipc_filter_message.c_str(), ipc_filter_message.size());

  if (!flattenable)
    return;

  SkBitmap bitmap;
  bitmap.allocN32Pixels(kBitmapSize, kBitmapSize);
  SkCanvas canvas(bitmap);
  canvas.clear(0x00000000);
  SkPaint paint;
  paint.setImageFilter(flattenable);
  canvas.save();
  canvas.clipRect(SkRect::MakeXYWH(0, 0, SkIntToScalar(kBitmapSize),
                                   SkIntToScalar(kBitmapSize)));
  canvas.drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &paint);
  canvas.restore();
}
