| // Copyright 2017 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 "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/process/memory.h" |
| #include "base/test/test_discardable_memory_allocator.h" |
| #include "cc/paint/paint_cache.h" |
| #include "cc/paint/paint_op_buffer.h" |
| #include "cc/test/transfer_cache_test_helper.h" |
| #include "components/viz/test/test_context_provider.h" |
| #include "gpu/command_buffer/common/buffer.h" |
| #include "gpu/command_buffer/service/service_font_manager.h" |
| #include "third_party/skia/include/core/SkSurface.h" |
| #include "third_party/skia/include/gpu/GrDirectContext.h" |
| |
| struct Environment { |
| Environment() { |
| // Disable noisy logging as per "libFuzzer in Chrome" documentation: |
| // testing/libfuzzer/getting_started.md#Disable-noisy-error-message-logging. |
| logging::SetMinLogLevel(logging::LOG_FATAL); |
| |
| base::EnableTerminationOnOutOfMemory(); |
| base::DiscardableMemoryAllocator::SetInstance( |
| &discardable_memory_allocator); |
| } |
| |
| ~Environment() { base::DiscardableMemoryAllocator::SetInstance(nullptr); } |
| |
| private: |
| base::TestDiscardableMemoryAllocator discardable_memory_allocator; |
| }; |
| |
| class FontSupport : public gpu::ServiceFontManager::Client { |
| public: |
| FontSupport() = default; |
| ~FontSupport() override = default; |
| |
| // gpu::ServiceFontManager::Client implementation. |
| scoped_refptr<gpu::Buffer> GetShmBuffer(uint32_t shm_id) override { |
| auto it = buffers_.find(shm_id); |
| if (it != buffers_.end()) |
| return it->second; |
| return CreateBuffer(shm_id); |
| } |
| void ReportProgress() override {} |
| |
| private: |
| scoped_refptr<gpu::Buffer> CreateBuffer(uint32_t shm_id) { |
| static const size_t kBufferSize = 2048u; |
| base::UnsafeSharedMemoryRegion shared_memory = |
| base::UnsafeSharedMemoryRegion::Create(kBufferSize); |
| base::WritableSharedMemoryMapping mapping = shared_memory.Map(); |
| auto buffer = gpu::MakeBufferFromSharedMemory(std::move(shared_memory), |
| std::move(mapping)); |
| buffers_[shm_id] = buffer; |
| return buffer; |
| } |
| |
| base::flat_map<uint32_t, scoped_refptr<gpu::Buffer>> buffers_; |
| }; |
| |
| void Raster(scoped_refptr<viz::TestContextProvider> context_provider, |
| SkStrikeClient* strike_client, |
| cc::ServicePaintCache* paint_cache, |
| const uint8_t* data, |
| size_t size) { |
| const size_t kRasterDimension = 32; |
| const size_t kMaxSerializedSize = 1000000; |
| |
| SkImageInfo image_info = SkImageInfo::MakeN32( |
| kRasterDimension, kRasterDimension, kOpaque_SkAlphaType); |
| context_provider->BindToCurrentThread(); |
| sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget( |
| context_provider->GrContext(), SkBudgeted::kYes, image_info); |
| SkCanvas* canvas = surface->getCanvas(); |
| |
| cc::PlaybackParams params(nullptr, canvas->getLocalToDevice()); |
| cc::TransferCacheTestHelper transfer_cache_helper; |
| std::vector<uint8_t> scratch_buffer; |
| cc::PaintOp::DeserializeOptions deserialize_options( |
| &transfer_cache_helper, paint_cache, strike_client, &scratch_buffer, |
| true /* is_privileged */, nullptr /* shared_image_provider */); |
| |
| // Need 4 bytes to be able to read the type/skip. |
| while (size >= 4) { |
| const cc::PaintOp* serialized = reinterpret_cast<const cc::PaintOp*>(data); |
| if (serialized->skip > kMaxSerializedSize) |
| break; |
| |
| std::unique_ptr<char, base::AlignedFreeDeleter> deserialized( |
| static_cast<char*>(base::AlignedAlloc( |
| sizeof(cc::LargestPaintOp), cc::PaintOpBuffer::PaintOpAlign))); |
| size_t bytes_read = 0; |
| cc::PaintOp* deserialized_op = cc::PaintOp::Deserialize( |
| data, size, deserialized.get(), sizeof(cc::LargestPaintOp), &bytes_read, |
| deserialize_options); |
| |
| if (!deserialized_op) |
| break; |
| |
| deserialized_op->Raster(canvas, params); |
| |
| deserialized_op->DestroyThis(); |
| |
| if (serialized->skip >= size) |
| break; |
| |
| size -= bytes_read; |
| data += bytes_read; |
| } |
| } |
| |
| // Deserialize an arbitrary number of cc::PaintOps and raster them |
| // using gpu raster into an SkCanvas. |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
| if (size <= sizeof(size_t)) |
| return 0; |
| |
| static Environment* env = new Environment(); |
| ALLOW_UNUSED_LOCAL(env); |
| base::CommandLine::Init(0, nullptr); |
| |
| // Partition the data to use some bytes for populating the font cache. |
| uint32_t bytes_for_fonts = data[0]; |
| if (bytes_for_fonts > size) |
| bytes_for_fonts = size / 2; |
| |
| FontSupport font_support; |
| scoped_refptr<gpu::ServiceFontManager> font_manager( |
| new gpu::ServiceFontManager(&font_support, |
| false /* disable_oopr_debug_crash_dump */)); |
| cc::ServicePaintCache paint_cache; |
| std::vector<SkDiscardableHandleId> locked_handles; |
| if (bytes_for_fonts > 0u) { |
| font_manager->Deserialize(reinterpret_cast<const char*>(data), |
| bytes_for_fonts, &locked_handles); |
| data += bytes_for_fonts; |
| size -= bytes_for_fonts; |
| } |
| |
| auto context_provider_no_support = viz::TestContextProvider::Create(); |
| context_provider_no_support->BindToCurrentThread(); |
| CHECK(!context_provider_no_support->GrContext()->supportsDistanceFieldText()); |
| Raster(context_provider_no_support, font_manager->strike_client(), |
| &paint_cache, data, size); |
| |
| auto context_provider_with_support = viz::TestContextProvider::Create( |
| std::string("GL_OES_standard_derivatives")); |
| context_provider_with_support->BindToCurrentThread(); |
| CHECK( |
| context_provider_with_support->GrContext()->supportsDistanceFieldText()); |
| Raster(context_provider_with_support, font_manager->strike_client(), |
| &paint_cache, data, size); |
| |
| font_manager->Unlock(locked_handles); |
| font_manager->Destroy(); |
| return 0; |
| } |