blob: f7d8f486c70466ee21ecf34c95eee73b4d5c737a [file] [log] [blame]
// Copyright 2016 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 <memory>
#include <vector>
#include "base/at_exit.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "build/build_config.h"
#include "gpu/command_buffer/common/constants.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/common/sync_token.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/image_manager.h"
#include "gpu/command_buffer/service/logger.h"
#include "gpu/command_buffer/service/mailbox_manager_impl.h"
#include "gpu/command_buffer/service/service_discardable_manager.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "gpu/command_buffer/service/transfer_buffer_manager.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_image_ref_counted_memory.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/init/gl_factory.h"
#include "ui/gl/test/gl_surface_test_support.h"
namespace gpu {
namespace {
const size_t kCommandBufferSize = 16384;
const size_t kTransferBufferSize = 16384;
const size_t kSmallTransferBufferSize = 16;
const size_t kTinyTransferBufferSize = 3;
#if !defined(GPU_FUZZER_USE_ANGLE) && !defined(GPU_FUZZER_USE_SWIFTSHADER)
#define GPU_FUZZER_USE_STUB
#endif
#if defined(GPU_FUZZER_USE_STUB)
static const char kExtensions[] =
"GL_AMD_compressed_ATC_texture "
"GL_ANGLE_texture_compression_dxt3 "
"GL_ANGLE_texture_compression_dxt5 "
"GL_ANGLE_texture_usage "
"GL_APPLE_ycbcr_422 "
"GL_ARB_texture_rectangle "
"GL_EXT_blend_func_extended "
"GL_EXT_color_buffer_float "
"GL_EXT_disjoint_timer_query "
"GL_EXT_draw_buffers "
"GL_EXT_frag_depth "
"GL_EXT_multisample_compatibility "
"GL_EXT_multisampled_render_to_texture "
"GL_EXT_occlusion_query_boolean "
"GL_EXT_read_format_bgra "
"GL_EXT_shader_texture_lod "
"GL_EXT_texture_compression_s3tc "
"GL_EXT_texture_filter_anisotropic "
"GL_IMG_texture_compression_pvrtc "
"GL_KHR_blend_equation_advanced "
"GL_KHR_blend_equation_advanced_coherent "
"GL_KHR_texture_compression_astc_ldr "
"GL_NV_EGL_stream_consumer_external "
"GL_NV_framebuffer_mixed_samples "
"GL_NV_path_rendering "
"GL_OES_compressed_ETC1_RGB8_texture "
"GL_OES_depth24 "
"GL_OES_EGL_image_external "
"GL_OES_rgb8_rgba8 "
"GL_OES_texture_float "
"GL_OES_texture_float_linear "
"GL_OES_texture_half_float "
"GL_OES_texture_half_float_linear";
#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()
: atexit_manager_(),
gpu_preferences_(GetGpuPreferences()),
share_group_(new gl::GLShareGroup),
translator_cache_(gpu_preferences_) {
logging::SetMinLogLevel(logging::LOG_FATAL);
base::CommandLine::Init(0, NULL);
auto* command_line = base::CommandLine::ForCurrentProcess();
ALLOW_UNUSED_LOCAL(command_line);
#if defined(GPU_FUZZER_USE_PASSTHROUGH_CMD_DECODER)
command_line->AppendSwitch(switches::kUsePassthroughCmdDecoder);
recreate_context_ = true;
#endif
#if defined(GPU_FUZZER_USE_ANGLE)
command_line->AppendSwitchASCII(switches::kUseGL,
gl::kGLImplementationANGLEName);
command_line->AppendSwitchASCII(switches::kUseANGLE,
gl::kANGLEImplementationNullName);
CHECK(gl::init::InitializeGLOneOffImplementation(
gl::kGLImplementationEGLGLES2, false, false, false));
#elif defined(GPU_FUZZER_USE_SWIFTSHADER)
command_line->AppendSwitchASCII(switches::kUseGL,
gl::kGLImplementationSwiftShaderName);
CHECK(gl::init::InitializeGLOneOffImplementation(
gl::kGLImplementationSwiftShaderGL, false, false, false));
#endif
#if !defined(GPU_FUZZER_USE_STUB)
surface_ = new gl::PbufferGLSurfaceEGL(gfx::Size());
surface_->Initialize();
if (!recreate_context_) {
InitContext();
}
#else // defined(GPU_FUZZER_USE_STUB)
surface_ = new gl::GLSurfaceStub;
InitContext();
gl::GLSurfaceTestSupport::InitializeOneOffWithMockBindings();
#endif // defined(GPU_FUZZER_USE_STUB)
}
void InitDecoder() {
if (recreate_context_) {
InitContext();
}
context_->MakeCurrent(surface_.get());
scoped_refptr<gles2::FeatureInfo> feature_info =
new gles2::FeatureInfo();
scoped_refptr<gles2::ContextGroup> context_group = new gles2::ContextGroup(
gpu_preferences_, &mailbox_manager_, nullptr /* memory_tracker */,
&translator_cache_, &completeness_cache_, feature_info,
true /* bind_generates_resource */, &image_manager_,
nullptr /* image_factory */, nullptr /* progress_reporter */,
GpuFeatureInfo(), &discardable_manager_);
command_buffer_.reset(new CommandBufferDirect(
context_group->transfer_buffer_manager(), &sync_point_manager_));
decoder_.reset(gles2::GLES2Decoder::Create(command_buffer_.get(),
command_buffer_->service(),
context_group.get()));
command_buffer_->set_handler(decoder_.get());
InitializeInitialCommandBuffer();
decoder_->GetLogger()->set_log_synthesized_gl_errors(false);
gles2::ContextCreationAttribHelper attrib_helper;
attrib_helper.offscreen_framebuffer_size = gfx::Size(16, 16);
attrib_helper.red_size = 8;
attrib_helper.green_size = 8;
attrib_helper.blue_size = 8;
attrib_helper.alpha_size = 8;
attrib_helper.depth_size = 0;
attrib_helper.stencil_size = 0;
#if defined(GPU_FUZZER_USE_SWIFTSHADER)
attrib_helper.context_type = gles2::CONTEXT_TYPE_OPENGLES2;
#else
attrib_helper.context_type = gles2::CONTEXT_TYPE_OPENGLES3;
#endif
bool result =
decoder_->Initialize(surface_.get(), context_.get(), true,
gles2::DisallowedFeatures(), attrib_helper);
CHECK(result);
decoder_->set_max_bucket_size(8 << 20);
context_group->buffer_manager()->set_max_buffer_size(8 << 20);
if (!vertex_translator_) {
// 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.
vertex_translator_ = decoder_->GetTranslator(GL_VERTEX_SHADER);
fragment_translator_ = decoder_->GetTranslator(GL_FRAGMENT_SHADER);
}
decoder_->MakeCurrent();
}
void ResetDecoder() {
decoder_->Destroy(true);
decoder_.reset();
if (recreate_context_) {
context_->ReleaseCurrent(nullptr);
context_ = nullptr;
}
command_buffer_.reset();
}
void RunCommandBuffer(const uint8_t* data, size_t size) {
// 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;
InitDecoder();
size_t buffer_size = buffer_->size();
CHECK_LE(padded_size, buffer_size);
command_buffer_->SetGetBuffer(buffer_id_);
auto* memory = static_cast<char*>(buffer_->memory());
memcpy(memory, data, size);
if (size < buffer_size)
memset(memory + size, 0, buffer_size - size);
command_buffer_->Flush(padded_size / 4);
ResetDecoder();
}
private:
void CreateTransferBuffer(size_t size, int32_t id) {
scoped_refptr<Buffer> buffer =
command_buffer_->CreateTransferBufferWithId(size, id);
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);
}
void InitContext() {
#if !defined(GPU_FUZZER_USE_STUB)
context_ = new gl::GLContextEGL(share_group_.get());
context_->Initialize(surface_.get(), gl::GLContextAttribs());
#else
scoped_refptr<gl::GLContextStub> context_stub =
new gl::GLContextStub(share_group_.get());
context_stub->SetGLVersionString("OpenGL ES 3.1");
context_stub->SetExtensionsString(kExtensions);
context_stub->SetUseStubApi(true);
context_ = context_stub;
#endif
}
base::AtExitManager atexit_manager_;
GpuPreferences gpu_preferences_;
gles2::MailboxManagerImpl mailbox_manager_;
scoped_refptr<gl::GLShareGroup> share_group_;
SyncPointManager sync_point_manager_;
gles2::ImageManager image_manager_;
ServiceDiscardableManager discardable_manager_;
bool recreate_context_ = false;
scoped_refptr<gl::GLSurface> surface_;
scoped_refptr<gl::GLContext> context_;
gles2::ShaderTranslatorCache translator_cache_;
gles2::FramebufferCompletenessCache completeness_cache_;
std::unique_ptr<CommandBufferDirect> command_buffer_;
std::unique_ptr<gles2::GLES2Decoder> decoder_;
scoped_refptr<gles2::ShaderTranslatorInterface> vertex_translator_;
scoped_refptr<gles2::ShaderTranslatorInterface> fragment_translator_;
scoped_refptr<Buffer> buffer_;
int32_t buffer_id_ = 0;
};
// 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;
}