| // 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 <memory> | |
| #include <string_view> | |
| #include <vector> | |
| #include "base/at_exit.h" | |
| #include "base/command_line.h" | |
| #include "base/compiler_specific.h" | |
| #include "base/functional/bind.h" | |
| #include "base/functional/callback_helpers.h" | |
| #include "base/i18n/icu_util.h" | |
| #include "base/logging.h" | |
| #include "base/memory/raw_ptr.h" | |
| #include "base/memory/ref_counted.h" | |
| #include "base/strings/string_util.h" | |
| #include "base/task/single_thread_task_executor.h" | |
| #include "build/build_config.h" | |
| #include "gpu/command_buffer/common/constants.h" | |
| #include "gpu/command_buffer/common/context_creation_attribs.h" | |
| #include "gpu/command_buffer/common/shared_image_usage.h" | |
| #include "gpu/command_buffer/service/buffer_manager.h" | |
| #include "gpu/command_buffer/service/command_buffer_direct.h" | |
| #include "gpu/command_buffer/service/context_group.h" | |
| #include "gpu/command_buffer/service/gles2_cmd_decoder.h" | |
| #include "gpu/command_buffer/service/gpu_switches.h" | |
| #include "gpu/command_buffer/service/gpu_tracer.h" | |
| #include "gpu/command_buffer/service/logger.h" | |
| #include "gpu/command_buffer/service/raster_decoder.h" | |
| #include "gpu/command_buffer/service/shared_context_state.h" | |
| #include "gpu/command_buffer/service/shared_image/shared_image_factory.h" | |
| #include "gpu/command_buffer/service/shared_image/shared_image_manager.h" | |
| #include "gpu/command_buffer/service/transfer_buffer_manager.h" | |
| #include "gpu/ipc/common/surface_handle.h" | |
| #include "ui/gfx/geometry/size.h" | |
| #include "ui/gl/gl_context.h" | |
| #include "ui/gl/gl_context_egl.h" | |
| #include "ui/gl/gl_context_stub.h" | |
| #include "ui/gl/gl_share_group.h" | |
| #include "ui/gl/gl_stub_api.h" | |
| #include "ui/gl/gl_surface.h" | |
| #include "ui/gl/gl_surface_egl.h" | |
| #include "ui/gl/gl_surface_stub.h" | |
| #include "ui/gl/gl_utils.h" | |
| #include "ui/gl/init/gl_factory.h" | |
| #include "ui/gl/test/gl_surface_test_support.h" | |
| #if BUILDFLAG(IS_OZONE) | |
| #include "ui/ozone/public/ozone_platform.h" | |
| #endif | |
| namespace gpu { | |
| namespace { | |
| const uint32_t kCommandBufferSize = 16384; | |
| const uint32_t kTransferBufferSize = 16384; | |
| const uint32_t kSmallTransferBufferSize = 16; | |
| const uint32_t kTinyTransferBufferSize = 3; | |
| #if !defined(GPU_FUZZER_USE_ANGLE) && !defined(GPU_FUZZER_USE_SWANGLE) | |
| #define GPU_FUZZER_USE_STUB | |
| #endif | |
| constexpr const char* kExtensions[] = { | |
| "GL_AMD_compressed_ATC_texture", | |
| "GL_ANGLE_client_arrays", | |
| "GL_ANGLE_depth_texture", | |
| "GL_ANGLE_framebuffer_multisample", | |
| "GL_ANGLE_instanced_arrays", | |
| "GL_ANGLE_request_extension", | |
| "GL_ANGLE_robust_client_memory", | |
| "GL_ANGLE_robust_resource_initialization", | |
| "GL_ANGLE_texture_compression_dxt3", | |
| "GL_ANGLE_texture_compression_dxt5", | |
| "GL_ANGLE_texture_rectangle", | |
| "GL_ANGLE_texture_usage", | |
| "GL_ANGLE_translated_shader_source", | |
| "GL_ANGLE_webgl_compatibility", | |
| "GL_APPLE_texture_format_BGRA8888", | |
| "GL_APPLE_vertex_array_object", | |
| "GL_APPLE_ycbcr_422", | |
| "GL_ARB_blend_func_extended", | |
| "GL_ARB_depth_texture", | |
| "GL_ARB_draw_buffers", | |
| "GL_ARB_draw_instanced", | |
| "GL_ARB_ES3_compatibility", | |
| "GL_ARB_explicit_attrib_location", | |
| "GL_ARB_explicit_uniform_location", | |
| "GL_ARB_framebuffer_sRGB", | |
| "GL_ARB_instanced_arrays", | |
| "GL_ARB_map_buffer_range", | |
| "GL_ARB_occlusion_query", | |
| "GL_ARB_occlusion_query2", | |
| "GL_ARB_pixel_buffer_object", | |
| "GL_ARB_program_interface_query", | |
| "GL_ARB_robustness", | |
| "GL_ARB_sampler_objects", | |
| "GL_ARB_shader_image_load_store", | |
| "GL_ARB_shading_language_420pack", | |
| "GL_ARB_texture_float", | |
| "GL_ARB_texture_gather", | |
| "GL_ARB_texture_non_power_of_two", | |
| "GL_ARB_texture_rectangle", | |
| "GL_ARB_texture_rg", | |
| "GL_ARB_texture_storage", | |
| "GL_ARB_timer_query", | |
| "GL_ARB_vertex_array_object", | |
| "GL_ATI_texture_compression_atitc", | |
| "GL_CHROMIUM_bind_generates_resource", | |
| "GL_CHROMIUM_color_buffer_float_rgb", | |
| "GL_CHROMIUM_color_buffer_float_rgba", | |
| "GL_CHROMIUM_copy_compressed_texture", | |
| "GL_CHROMIUM_copy_texture", | |
| "GL_EXT_blend_func_extended", | |
| "GL_EXT_blend_minmax", | |
| "GL_EXT_float_blend", | |
| "GL_EXT_color_buffer_float", | |
| "GL_EXT_color_buffer_half_float", | |
| "GL_EXT_debug_marker", | |
| "GL_EXT_direct_state_access", | |
| "GL_EXT_discard_framebuffer", | |
| "GL_EXT_disjoint_timer_query", | |
| "GL_EXT_draw_buffers", | |
| "GL_EXT_frag_depth", | |
| "GL_EXT_framebuffer_multisample", | |
| "GL_EXT_framebuffer_sRGB", | |
| "GL_EXT_map_buffer_range", | |
| "GL_EXT_multisample_compatibility", | |
| "GL_EXT_multisampled_render_to_texture", | |
| "GL_EXT_occlusion_query_boolean", | |
| "GL_EXT_packed_depth_stencil", | |
| "GL_EXT_read_format_bgra", | |
| "GL_EXT_robustness", | |
| "GL_EXT_shader_texture_lod", | |
| "GL_EXT_sRGB", | |
| "GL_EXT_sRGB_write_control", | |
| "GL_EXT_texture_compression_bptc", | |
| "GL_EXT_texture_compression_rgtc", | |
| "GL_EXT_texture_compression_dxt1", | |
| "GL_EXT_texture_compression_s3tc", | |
| "GL_EXT_texture_compression_s3tc_srgb", | |
| "GL_EXT_texture_filter_anisotropic", | |
| "GL_EXT_texture_format_BGRA8888", | |
| "GL_EXT_texture_norm16", | |
| "GL_EXT_texture_rg", | |
| "GL_EXT_texture_sRGB", | |
| "GL_EXT_texture_sRGB_decode", | |
| "GL_EXT_texture_storage", | |
| "GL_EXT_timer_query", | |
| "GL_IMG_multisampled_render_to_texture", | |
| "GL_IMG_texture_compression_pvrtc", | |
| "GL_INTEL_framebuffer_CMAA", | |
| "GL_KHR_blend_equation_advanced", | |
| "GL_KHR_blend_equation_advanced_coherent", | |
| "GL_KHR_debug", | |
| "GL_KHR_robustness", | |
| "GL_KHR_texture_compression_astc_ldr", | |
| "GL_NV_blend_equation_advanced", | |
| "GL_NV_blend_equation_advanced_coherent", | |
| "GL_NV_draw_buffers", | |
| "GL_NV_EGL_stream_consumer_external", | |
| "GL_NV_fence", | |
| "GL_NV_framebuffer_mixed_samples", | |
| "GL_NV_path_rendering", | |
| "GL_NV_pixel_buffer_object", | |
| "GL_NV_sRGB_formats", | |
| "GL_OES_compressed_ETC1_RGB8_texture", | |
| "GL_OES_depth24", | |
| "GL_OES_depth_texture", | |
| "GL_OES_draw_buffers_indexed", | |
| "GL_OES_EGL_image_external", | |
| "GL_OES_element_index_uint", | |
| "GL_OES_fbo_render_mipmap", | |
| "GL_OES_packed_depth_stencil", | |
| "GL_OES_rgb8_rgba8", | |
| "GL_OES_standard_derivatives", | |
| "GL_OES_texture_float", | |
| "GL_OES_texture_float_linear", | |
| "GL_OES_texture_half_float", | |
| "GL_OES_texture_half_float_linear", | |
| "GL_OES_texture_npot", | |
| "GL_OES_vertex_array_object"}; | |
| constexpr size_t kExtensionCount = std::size(kExtensions); | |
| #if defined(GPU_FUZZER_USE_STUB) | |
| constexpr const char* kDriverVersions[] = {"OpenGL ES 2.0", "OpenGL ES 3.1", | |
| "2.0", "4.5.0"}; | |
| #endif | |
| class BitIterator { | |
| public: | |
| BitIterator(const uint8_t* data, size_t size) : data_(data), size_(size) {} | |
| bool GetBit() { | |
| if (offset_ == size_) | |
| return false; | |
| bool value = !!(UNSAFE_TODO(data_[offset_]) & (1u << bit_)); | |
| if (++bit_ == 8) { | |
| bit_ = 0; | |
| ++offset_; | |
| } | |
| return value; | |
| } | |
| void Advance(size_t bits) { | |
| bit_ += bits; | |
| offset_ += bit_ / 8; | |
| if (offset_ >= size_) { | |
| offset_ = size_; | |
| bit_ = 0; | |
| } else { | |
| bit_ = bit_ % 8; | |
| } | |
| } | |
| size_t consumed_bytes() const { return offset_ + (bit_ + 7) / 8; } | |
| private: | |
| const uint8_t* data_; | |
| size_t size_; | |
| size_t offset_ = 0; | |
| size_t bit_ = 0; | |
| }; | |
| struct Config { | |
| size_t MakeFromBits(const uint8_t* bits, size_t size) { | |
| BitIterator it(bits, size); | |
| [[maybe_unused]] bool es3 = it.GetBit(); | |
| #if defined(GPU_FUZZER_USE_RASTER_DECODER) | |
| context_type = CONTEXT_TYPE_OPENGLES2; | |
| #else | |
| if (es3) { | |
| context_type = CONTEXT_TYPE_OPENGLES3; | |
| } else { | |
| context_type = CONTEXT_TYPE_OPENGLES2; | |
| } | |
| #endif | |
| #if defined(GPU_FUZZER_USE_STUB) | |
| std::vector<std::string_view> enabled_extensions; | |
| enabled_extensions.reserve(kExtensionCount); | |
| for (const char* extension : kExtensions) { | |
| if (it.GetBit()) | |
| enabled_extensions.push_back(extension); | |
| } | |
| extensions = base::JoinString(enabled_extensions, " "); | |
| bool desktop_driver = it.GetBit(); | |
| size_t version_index = (desktop_driver ? 2 : 0) + (es3 ? 1 : 0); | |
| version = UNSAFE_TODO(kDriverVersions[version_index]); | |
| #else | |
| // We consume bits even if we don't use them, so that the same inputs can be | |
| // used for every fuzzer variation | |
| it.Advance(kExtensionCount + 1); | |
| #endif | |
| #define GPU_OP(type, name) workarounds.name = it.GetBit(); | |
| GPU_DRIVER_BUG_WORKAROUNDS(GPU_OP) | |
| #undef GPU_OP | |
| #if defined(GPU_FUZZER_USE_PASSTHROUGH_CMD_DECODER) && \ | |
| !defined(GPU_FUZZER_USE_RASTER_DECODER) | |
| gl_context_attribs.webgl_compatibility_context = | |
| IsWebGLContextType(context_type); | |
| gl_context_attribs.hardened_context = | |
| !gl_context_attribs.webgl_compatibility_context; | |
| gl_context_attribs.global_texture_share_group = true; | |
| gl_context_attribs.robust_resource_initialization = true; | |
| gl_context_attribs.robust_buffer_access = true; | |
| gl_context_attribs.allow_client_arrays = false; | |
| gl_context_attribs.client_major_es_version = | |
| IsWebGL2OrES3OrHigherContextType(context_type) ? 3 : 2; | |
| gl_context_attribs.client_minor_es_version = 0; | |
| #endif | |
| return it.consumed_bytes(); | |
| } | |
| ContextType context_type = CONTEXT_TYPE_OPENGLES2; | |
| GpuDriverBugWorkarounds workarounds; | |
| gl::GLContextAttribs gl_context_attribs; | |
| #if defined(GPU_FUZZER_USE_STUB) | |
| const char* version; | |
| std::string extensions; | |
| #endif | |
| }; | |
| GpuPreferences GetGpuPreferences() { | |
| GpuPreferences preferences; | |
| #if defined(GPU_FUZZER_USE_PASSTHROUGH_CMD_DECODER) | |
| preferences.use_passthrough_cmd_decoder = true; | |
| #endif | |
| return preferences; | |
| } | |
| class CommandBufferSetup { | |
| public: | |
| CommandBufferSetup() | |
| : at_exit_manager_(), | |
| #if BUILDFLAG(IS_MAC) | |
| task_executor_(base::MessagePumpType::NS_RUNLOOP), | |
| #endif | |
| gpu_preferences_(GetGpuPreferences()), | |
| share_group_(new gl::GLShareGroup), | |
| translator_cache_(gpu_preferences_) { | |
| logging::SetMinLogLevel(logging::LOGGING_FATAL); | |
| CHECK(base::i18n::InitializeICU()); | |
| base::CommandLine::Init(0, nullptr); | |
| [[maybe_unused]] auto* command_line = | |
| base::CommandLine::ForCurrentProcess(); | |
| #if BUILDFLAG(IS_OZONE) | |
| ui::OzonePlatform::InitializeForGPU(ui::OzonePlatform::InitParams()); | |
| #endif | |
| #if defined(GPU_FUZZER_USE_ANGLE) | |
| command_line->AppendSwitchASCII(switches::kUseGL, | |
| gl::kGLImplementationANGLEName); | |
| #if defined(GPU_FUZZER_USE_SWANGLE) | |
| command_line->AppendSwitchASCII(switches::kUseANGLE, | |
| gl::kANGLEImplementationSwiftShaderName); | |
| #else | |
| command_line->AppendSwitchASCII(switches::kUseANGLE, | |
| gl::kANGLEImplementationNullName); | |
| #endif | |
| CHECK(gl::init::InitializeStaticGLBindingsImplementation( | |
| gl::GLImplementationParts(gl::kGLImplementationEGLANGLE))); | |
| display_ = gl::init::InitializeGLOneOffPlatformImplementation( | |
| /*disable_gl_drawing=*/false, | |
| /*init_extensions=*/true, | |
| /*gpu_preference=*/gl::GpuPreference::kDefault); | |
| CHECK(display_); | |
| #elif defined(GPU_FUZZER_USE_STUB) | |
| gl::GLSurfaceTestSupport::InitializeOneOffWithStubBindings(); | |
| // Because the context depends on configuration bits, we want to recreate | |
| // it every time. | |
| recreate_context_ = true; | |
| #else | |
| #error invalid configuration | |
| #endif | |
| if (gpu_preferences_.use_passthrough_cmd_decoder) | |
| recreate_context_ = true; | |
| if (!recreate_context_) { | |
| InitContext(); | |
| } | |
| } | |
| ~CommandBufferSetup() { | |
| if (display_) { | |
| gl::init::ShutdownGL(display_, false); | |
| display_ = nullptr; | |
| } | |
| } | |
| bool InitDecoder() { | |
| if (!context_) { | |
| InitContext(); | |
| } | |
| context_->MakeCurrentDefault(); | |
| GpuFeatureInfo gpu_feature_info; | |
| #if defined(GPU_FUZZER_USE_RASTER_DECODER) | |
| gpu_feature_info.status_values[GPU_FEATURE_TYPE_GPU_TILE_RASTERIZATION] = | |
| kGpuFeatureStatusEnabled; | |
| #endif | |
| command_buffer_.reset(new CommandBufferDirect()); | |
| if (gpu_preferences_.use_passthrough_cmd_decoder) { | |
| // Virtualized contexts don't work with passthrough command decoder. | |
| // See https://crbug.com/914976 | |
| config_.workarounds.use_virtualized_gl_contexts = false; | |
| } | |
| scoped_refptr<gl::GLContext> shared_context; | |
| if (config_.workarounds.use_virtualized_gl_contexts) { | |
| shared_context = context_; | |
| } else { | |
| shared_context = CreateContext(); | |
| } | |
| shared_context->MakeCurrentDefault(); | |
| context_state_ = base::MakeRefCounted<SharedContextState>( | |
| share_group_, shared_context->default_surface(), | |
| std::move(shared_context), | |
| config_.workarounds.use_virtualized_gl_contexts, base::DoNothing(), | |
| gpu_preferences_.gr_context_type); | |
| context_state_->InitializeSkia(gpu_preferences_, config_.workarounds); | |
| context_state_->InitializeGL(gpu_preferences_, config_.workarounds, | |
| gpu_feature_info); | |
| shared_image_manager_ = std::make_unique<SharedImageManager>(); | |
| shared_image_factory_ = std::make_unique<SharedImageFactory>( | |
| gpu_preferences_, config_.workarounds, gpu_feature_info, | |
| context_state_.get(), shared_image_manager_.get(), | |
| /*memory_tracker=*/nullptr, | |
| /*is_for_display_compositor=*/false); | |
| for (uint32_t usage = SHARED_IMAGE_USAGE_GLES2_READ; | |
| usage <= LAST_CLIENT_USAGE; usage <<= 1) { | |
| Mailbox::Name name; | |
| UNSAFE_TODO(memset(name, 0, sizeof(name))); | |
| name[0] = usage; | |
| // Mark this as a SharedImage mailbox. | |
| constexpr size_t kSharedImageFlagIndex = GL_MAILBOX_SIZE_CHROMIUM - 1; | |
| constexpr int8_t kSharedImageFlag = 0x1; | |
| name[kSharedImageFlagIndex] |= kSharedImageFlag; | |
| Mailbox mailbox; | |
| mailbox.SetName(name); | |
| viz::SharedImageFormat si_format = viz::SinglePlaneFormat::kRGBA_8888; | |
| shared_image_factory_->CreateSharedImage( | |
| mailbox, | |
| SharedImageInfo(si_format, gfx::Size(256, 256), | |
| gfx::ColorSpace::CreateSRGB(), | |
| kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, | |
| SharedImageUsageSet(usage), "TestLabel"), | |
| gpu::kNullSurfaceHandle); | |
| } | |
| #if defined(GPU_FUZZER_USE_RASTER_DECODER) | |
| context_state_->MakeCurrent(nullptr); | |
| decoder_ = raster::RasterDecoder::Create( | |
| command_buffer_.get(), command_buffer_->service(), &outputter_, | |
| gpu_feature_info, gpu_preferences_, /*memory_tracker=*/nullptr, | |
| shared_image_manager_.get(), context_state_, /*is_privileged=*/true); | |
| decoder_->GetLogger()->set_log_synthesized_gl_errors(false); | |
| auto result = | |
| decoder_->Initialize(/*lose_context_when_out_of_memory=*/false); | |
| if (result != gpu::ContextResult::kSuccess) { | |
| return false; | |
| } | |
| #else | |
| context_->MakeCurrentDefault(); | |
| // GLES2Decoder may Initialize feature_info differently than | |
| // SharedContextState and should have its own. | |
| auto decoder_feature_info = base::MakeRefCounted<gles2::FeatureInfo>( | |
| config_.workarounds, gpu_feature_info); | |
| scoped_refptr<gles2::ContextGroup> context_group = | |
| MakeRefCounted<gles2::ContextGroup>( | |
| gpu_preferences_, /*memory_tracker=*/nullptr, &translator_cache_, | |
| &completeness_cache_, decoder_feature_info, | |
| /*progress_reporter=*/nullptr, gpu_feature_info, | |
| shared_image_manager_.get()); | |
| auto* context = context_.get(); | |
| decoder_ = gles2::GLES2Decoder::Create(command_buffer_.get(), | |
| command_buffer_->service(), | |
| &outputter_, context_group.get()); | |
| decoder_->GetLogger()->set_log_synthesized_gl_errors(false); | |
| auto result = | |
| decoder_->Initialize(context->default_surface(), context, | |
| /*offscreen=*/true, config_.context_type, | |
| /*lose_context_when_out_of_memory=*/false); | |
| if (result != gpu::ContextResult::kSuccess) { | |
| return false; | |
| } | |
| #endif | |
| decoder_initialized_ = true; | |
| command_buffer_->set_handler(decoder_.get()); | |
| InitializeInitialCommandBuffer(); | |
| decoder_->set_max_bucket_size(8 << 20); | |
| #if !defined(GPU_FUZZER_USE_RASTER_DECODER) | |
| if (context_group->buffer_manager()) { | |
| context_group->buffer_manager()->set_max_buffer_size(8 << 20); | |
| } | |
| #endif | |
| return decoder_->MakeCurrent(); | |
| } | |
| void ResetDecoder() { | |
| bool context_lost = false; | |
| if (decoder_) { | |
| #if !defined(GPU_FUZZER_USE_RASTER_DECODER) | |
| // Keep a reference to the translators, which keeps them in the cache even | |
| // after the decoder is reset. They are expensive to initialize, but they | |
| // don't keep state. | |
| scoped_refptr<gles2::ShaderTranslatorInterface> translator = | |
| decoder_->GetTranslator(GL_VERTEX_SHADER); | |
| if (translator) | |
| translator->AddRef(); | |
| translator = decoder_->GetTranslator(GL_FRAGMENT_SHADER); | |
| if (translator) | |
| translator->AddRef(); | |
| #endif | |
| context_lost = decoder_->WasContextLost(); | |
| // Only safe to call CheckResetStatus if !WasContextLost. | |
| if (!context_lost) | |
| context_lost = decoder_initialized_ && decoder_->CheckResetStatus(); | |
| // If |decoder_->Initialize(...)| was unsuccessful, |decoder_| would have | |
| // already called Destroy. | |
| if (decoder_initialized_) | |
| decoder_->Destroy(!context_lost); | |
| decoder_.reset(); | |
| if (!context_lost) | |
| context_lost = !context_state_->MakeCurrent(nullptr); | |
| shared_image_factory_->DestroyAllSharedImages(!context_lost); | |
| shared_image_factory_.reset(); | |
| shared_image_manager_.reset(); | |
| context_state_->MakeCurrent(nullptr); | |
| context_state_.reset(); | |
| } | |
| if (context_) { | |
| if (recreate_context_ || context_lost) { | |
| context_->ReleaseCurrent(nullptr); | |
| context_ = nullptr; | |
| } | |
| } | |
| command_buffer_.reset(); | |
| decoder_initialized_ = false; | |
| } | |
| void RunCommandBuffer(const uint8_t* data, size_t size) { | |
| size_t consumed = config_.MakeFromBits(data, size); | |
| consumed = (consumed + 3) & ~3; | |
| if (consumed > size) | |
| return; | |
| UNSAFE_TODO(data += consumed); | |
| size -= consumed; | |
| // The commands are flushed at a uint32_t granularity. If the data is not | |
| // a full command, we zero-pad it. | |
| size_t padded_size = (size + 3) & ~3; | |
| // crbug.com/638836 The -max_len argument is sometimes not respected, so the | |
| // fuzzer may give us too much data. Bail ASAP in that case. | |
| if (padded_size > kCommandBufferSize) | |
| return; | |
| if (!InitDecoder()) { | |
| ResetDecoder(); | |
| return; | |
| } | |
| uint32_t buffer_size = buffer_->size(); | |
| CHECK_LE(padded_size, buffer_size); | |
| command_buffer_->SetGetBuffer(buffer_id_); | |
| auto* memory = static_cast<char*>(buffer_->memory()); | |
| UNSAFE_TODO(memcpy(memory, data, size)); | |
| if (size < buffer_size) | |
| UNSAFE_TODO(memset(memory + size, 0, buffer_size - size)); | |
| command_buffer_->Flush(padded_size / 4); | |
| ResetDecoder(); | |
| } | |
| private: | |
| void CreateTransferBuffer(uint32_t size, int32_t id) { | |
| scoped_refptr<Buffer> buffer = | |
| command_buffer_->CreateTransferBufferWithId(size, id); | |
| UNSAFE_TODO(memset(buffer->memory(), 0, size)); | |
| } | |
| void InitializeInitialCommandBuffer() { | |
| buffer_id_ = 1; | |
| buffer_ = command_buffer_->CreateTransferBufferWithId(kCommandBufferSize, | |
| buffer_id_); | |
| CHECK(buffer_); | |
| // Create some transfer buffers. This is somewhat arbitrary, but having a | |
| // reasonably sized buffer in slot 4 allows us to prime the corpus with data | |
| // extracted from unit tests. | |
| CreateTransferBuffer(kTransferBufferSize, 2); | |
| CreateTransferBuffer(kSmallTransferBufferSize, 3); | |
| CreateTransferBuffer(kTransferBufferSize, 4); | |
| CreateTransferBuffer(kTinyTransferBufferSize, 5); | |
| } | |
| scoped_refptr<gl::GLContext> CreateContext() { | |
| // The surface will be owned by the |context|. | |
| auto surface = gl::init::CreateOffscreenGLSurface(display_, gfx::Size()); | |
| #if defined(GPU_FUZZER_USE_STUB) | |
| auto stub = base::MakeRefCounted<gl::GLContextStub>(share_group_.get()); | |
| stub->SetGLVersionString(config_.version); | |
| stub->SetExtensionsString(config_.extensions.c_str()); | |
| stub->SetUseStubApi(true); | |
| // The stub ctx needs to be initialized so that the gl::GLContext can | |
| // store the |compatible_surface|. | |
| stub->Initialize(surface.get(), {}); | |
| return stub; | |
| #else | |
| auto context = base::MakeRefCounted<gl::GLContextEGL>(share_group_.get()); | |
| context->Initialize(surface.get(), config_.gl_context_attribs); | |
| return context; | |
| #endif | |
| } | |
| void InitContext() { | |
| context_ = CreateContext(); | |
| // When not using the passthrough decoder, ANGLE should not be generating | |
| // errors (the decoder should prevent those from happening). We register a | |
| // callback to catch them if it does. | |
| #if defined(GPU_FUZZER_USE_ANGLE) && \ | |
| !defined(GPU_FUZZER_USE_PASSTHROUGH_CMD_DECODER) | |
| context_->MakeCurrentDefault(); | |
| glEnable(GL_DEBUG_OUTPUT); | |
| glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); | |
| glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, | |
| GL_FALSE); | |
| glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, | |
| GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE); | |
| glDebugMessageCallback(&LogGLDebugMessage, nullptr); | |
| #endif | |
| } | |
| static void GL_APIENTRY LogGLDebugMessage(GLenum source, | |
| GLenum type, | |
| GLuint id, | |
| GLenum severity, | |
| GLsizei length, | |
| const GLchar* message, | |
| const GLvoid* user_param) { | |
| LOG_IF(FATAL, (id != GL_OUT_OF_MEMORY)) << "GL Driver Message: " << message; | |
| } | |
| base::AtExitManager at_exit_manager_; | |
| #if BUILDFLAG(IS_MAC) | |
| // b/513273428, b/514834448 - For Mac only. |task_executor_| with | |
| // base::MessagePumpType::DEFAULT causes a ASAN crash on ChromeOS. | |
| base::SingleThreadTaskExecutor task_executor_; | |
| #endif | |
| GpuPreferences gpu_preferences_; | |
| Config config_; | |
| gles2::TraceOutputter outputter_; | |
| scoped_refptr<gl::GLShareGroup> share_group_; | |
| std::unique_ptr<SharedImageManager> shared_image_manager_; | |
| std::unique_ptr<SharedImageFactory> shared_image_factory_; | |
| bool recreate_context_ = false; | |
| raw_ptr<gl::GLDisplay> display_ = nullptr; | |
| scoped_refptr<gl::GLContext> context_; | |
| scoped_refptr<SharedContextState> context_state_; | |
| gles2::ShaderTranslatorCache translator_cache_; | |
| gles2::FramebufferCompletenessCache completeness_cache_; | |
| std::unique_ptr<CommandBufferDirect> command_buffer_; | |
| #if defined(GPU_FUZZER_USE_RASTER_DECODER) | |
| std::unique_ptr<raster::RasterDecoder> decoder_; | |
| #else | |
| std::unique_ptr<gles2::GLES2Decoder> decoder_; | |
| #endif | |
| scoped_refptr<Buffer> buffer_; | |
| int32_t buffer_id_ = 0; | |
| bool decoder_initialized_ = false; | |
| }; | |
| // Intentionally leaked because asan tries to exit cleanly after a crash, but | |
| // the decoder is usually in a bad state at that point. | |
| // We need to load ANGLE libraries before the fuzzer infrastructure starts, | |
| // because it gets confused about new coverage counters being dynamically | |
| // registered, causing crashes. | |
| CommandBufferSetup* g_setup = new CommandBufferSetup(); | |
| } // anonymous namespace | |
| } // namespace gpu | |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { | |
| gpu::g_setup->RunCommandBuffer(data, size); | |
| return 0; | |
| } |