Commit 28b6528c by Corentin Wallez Committed by Commit Bot

Add a fuzzer for the shader translator.

BUG=angleproject:1522 Change-Id: Idbe8194ba478366e99c7460d403d03fe27dd89d0 Reviewed-on: https://chromium-review.googlesource.com/353153 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 09cfac60
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
import("//build/config/dcheck_always_on.gni") import("//build/config/dcheck_always_on.gni")
import("//build/config/linux/pkg_config.gni") import("//build/config/linux/pkg_config.gni")
import("//build/config/ui.gni") import("//build/config/ui.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//third_party/angle/build/angle_common.gni") import("//third_party/angle/build/angle_common.gni")
import("//ui/ozone/ozone.gni") import("//ui/ozone/ozone.gni")
...@@ -114,6 +115,9 @@ static_library("preprocessor") { ...@@ -114,6 +115,9 @@ static_library("preprocessor") {
config("translator_static_config") { config("translator_static_config") {
defines = [ "ANGLE_TRANSLATOR_STATIC" ] defines = [ "ANGLE_TRANSLATOR_STATIC" ]
if (use_libfuzzer) {
defines += [ "ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC" ]
}
} }
config("debug_annotations_config") { config("debug_annotations_config") {
...@@ -176,19 +180,19 @@ static_library("translator_lib") { ...@@ -176,19 +180,19 @@ static_library("translator_lib") {
sources = rebase_path(compiler_gypi.angle_translator_lib_sources, ".", "src") sources = rebase_path(compiler_gypi.angle_translator_lib_sources, ".", "src")
defines = [] defines = []
if (angle_enable_essl) { if (angle_enable_essl || use_libfuzzer) {
sources += sources +=
rebase_path(compiler_gypi.angle_translator_lib_essl_sources, ".", "src") rebase_path(compiler_gypi.angle_translator_lib_essl_sources, ".", "src")
defines += [ "ANGLE_ENABLE_ESSL" ] defines += [ "ANGLE_ENABLE_ESSL" ]
} }
if (angle_enable_glsl) { if (angle_enable_glsl || use_libfuzzer) {
sources += sources +=
rebase_path(compiler_gypi.angle_translator_lib_glsl_sources, ".", "src") rebase_path(compiler_gypi.angle_translator_lib_glsl_sources, ".", "src")
defines += [ "ANGLE_ENABLE_GLSL" ] defines += [ "ANGLE_ENABLE_GLSL" ]
} }
if (angle_enable_hlsl) { if (angle_enable_hlsl || use_libfuzzer) {
sources += sources +=
rebase_path(compiler_gypi.angle_translator_lib_hlsl_sources, ".", "src") rebase_path(compiler_gypi.angle_translator_lib_hlsl_sources, ".", "src")
defines += [ "ANGLE_ENABLE_HLSL" ] defines += [ "ANGLE_ENABLE_HLSL" ]
...@@ -235,6 +239,21 @@ static_library("translator_static") { ...@@ -235,6 +239,21 @@ static_library("translator_static") {
] ]
} }
fuzzer_test("translator_fuzzer") {
sources = [
"src/compiler/fuzz/translator_fuzzer.cpp",
]
include_dirs = [
"include",
"src",
]
deps = [
":translator_static",
]
}
config("commit_id_config") { config("commit_id_config") {
include_dirs = [ "$root_gen_dir/angle" ] include_dirs = [ "$root_gen_dir/angle" ]
} }
......
//
// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// translator_fuzzer.cpp: A libfuzzer fuzzer for the shader translator.
#include <stddef.h>
#include <stdint.h>
#include <unordered_map>
#include <iostream>
#include "compiler/translator/Compiler.h"
#include "angle_gl.h"
struct TranslatorCacheKey
{
bool operator==(const TranslatorCacheKey &other) const
{
return type == other.type && spec == other.spec && output == other.output;
}
uint32_t type = 0;
uint32_t spec = 0;
uint32_t output = 0;
};
namespace std
{
template <>
struct hash<TranslatorCacheKey>
{
std::size_t operator()(const TranslatorCacheKey &k) const
{
return (hash<uint32_t>()(k.type) << 1) ^ (hash<uint32_t>()(k.spec) >> 1) ^
hash<uint32_t>()(k.output);
}
};
} // namespace std
static std::unordered_map<TranslatorCacheKey, TCompiler *> translators;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
// Reserve some size for future compile options
const size_t kHeaderSize = 128;
if (size <= kHeaderSize)
{
return 0;
}
// Make sure the rest of data will be a valid C string so that we don't have to copy it.
if (data[size - 1] != 0)
{
return 0;
}
uint32_t type = *reinterpret_cast<const uint32_t *>(data);
uint32_t spec = *reinterpret_cast<const uint32_t *>(data + 4);
uint32_t output = *reinterpret_cast<const uint32_t *>(data + 8);
uint64_t options = *reinterpret_cast<const uint64_t *>(data + 12);
if (type != GL_FRAGMENT_SHADER && type != GL_VERTEX_SHADER)
{
return 0;
}
if (spec != SH_GLES2_SPEC && type != SH_WEBGL_SPEC && spec != SH_GLES3_SPEC &&
spec != SH_WEBGL2_SPEC)
{
return 0;
}
std::vector<uint32_t> validOutputs;
validOutputs.push_back(SH_ESSL_OUTPUT);
validOutputs.push_back(SH_GLSL_COMPATIBILITY_OUTPUT);
validOutputs.push_back(SH_GLSL_130_OUTPUT);
validOutputs.push_back(SH_GLSL_140_OUTPUT);
validOutputs.push_back(SH_GLSL_150_CORE_OUTPUT);
validOutputs.push_back(SH_GLSL_330_CORE_OUTPUT);
validOutputs.push_back(SH_GLSL_400_CORE_OUTPUT);
validOutputs.push_back(SH_GLSL_410_CORE_OUTPUT);
validOutputs.push_back(SH_GLSL_420_CORE_OUTPUT);
validOutputs.push_back(SH_GLSL_430_CORE_OUTPUT);
validOutputs.push_back(SH_GLSL_440_CORE_OUTPUT);
validOutputs.push_back(SH_GLSL_450_CORE_OUTPUT);
validOutputs.push_back(SH_HLSL_OUTPUT);
validOutputs.push_back(SH_HLSL9_OUTPUT);
validOutputs.push_back(SH_HLSL11_OUTPUT);
validOutputs.push_back(SH_HLSL_3_0_OUTPUT);
validOutputs.push_back(SH_HLSL_4_1_OUTPUT);
validOutputs.push_back(SH_HLSL_4_0_FL9_3_OUTPUT);
bool found = false;
for (auto valid : validOutputs)
{
found = found || (valid == output);
}
if (!found)
{
return 0;
}
size -= kHeaderSize;
data += kHeaderSize;
if (!ShInitialize())
{
return 0;
}
TranslatorCacheKey key;
key.type = type;
key.spec = spec;
key.output = output;
if (translators.find(key) == translators.end())
{
TCompiler *translator = ConstructCompiler(type, static_cast<ShShaderSpec>(spec),
static_cast<ShShaderOutput>(output));
if (!translator)
{
return 0;
}
ShBuiltInResources resources;
ShInitBuiltInResources(&resources);
// Enable all the extensions to have more coverage
resources.OES_standard_derivatives = 1;
resources.OES_EGL_image_external = 1;
resources.OES_EGL_image_external_essl3 = 1;
resources.NV_EGL_stream_consumer_external = 1;
resources.ARB_texture_rectangle = 1;
resources.EXT_blend_func_extended = 1;
resources.EXT_draw_buffers = 1;
resources.EXT_frag_depth = 1;
resources.EXT_shader_texture_lod = 1;
resources.WEBGL_debug_shader_precision = 1;
resources.EXT_shader_framebuffer_fetch = 1;
resources.NV_shader_framebuffer_fetch = 1;
resources.ARM_shader_framebuffer_fetch = 1;
if (!translator->Init(resources))
{
DeleteCompiler(translator);
return 0;
}
translators[key] = translator;
}
TCompiler *translator = translators[key];
const char *shaderStrings[] = {reinterpret_cast<const char *>(data)};
translator->compile(shaderStrings, 1, options);
return 0;
}
...@@ -29,6 +29,46 @@ ...@@ -29,6 +29,46 @@
#include "angle_gl.h" #include "angle_gl.h"
#include "common/utilities.h" #include "common/utilities.h"
#include <iostream>
namespace
{
#if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
void DumpFuzzerCase(char const *const *shaderStrings,
size_t numStrings,
uint32_t type,
uint32_t spec,
uint32_t output,
uint64_t options)
{
static int fileIndex = 0;
std::ostringstream o;
o << "corpus/" << fileIndex++ << ".sample";
std::string s = o.str();
// Must match the input format of the fuzzer
FILE *f = fopen(s.c_str(), "w");
fwrite(&type, sizeof(type), 1, f);
fwrite(&spec, sizeof(spec), 1, f);
fwrite(&output, sizeof(output), 1, f);
fwrite(&options, sizeof(options), 1, f);
char zero[128 - 20] = {0};
fwrite(&zero, 128 - 20, 1, f);
for (size_t i = 0; i < numStrings; i++)
{
fwrite(shaderStrings[i], sizeof(char), strlen(shaderStrings[i]), f);
}
fwrite(&zero, 1, 1, f);
fclose(f);
}
#endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
} // anonymous namespace
bool IsWebGLBasedSpec(ShShaderSpec spec) bool IsWebGLBasedSpec(ShShaderSpec spec)
{ {
return (spec == SH_WEBGL_SPEC || spec == SH_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC); return (spec == SH_WEBGL_SPEC || spec == SH_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC);
...@@ -402,6 +442,10 @@ bool TCompiler::compile(const char *const shaderStrings[], ...@@ -402,6 +442,10 @@ bool TCompiler::compile(const char *const shaderStrings[],
size_t numStrings, size_t numStrings,
ShCompileOptions compileOptionsIn) ShCompileOptions compileOptionsIn)
{ {
#if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
DumpFuzzerCase(shaderStrings, numStrings, shaderType, shaderSpec, outputType, compileOptionsIn);
#endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
if (numStrings == 0) if (numStrings == 0)
return true; return true;
......
...@@ -50,29 +50,18 @@ void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator) ...@@ -50,29 +50,18 @@ void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator)
// Implement the functionality of the TPoolAllocator class, which // Implement the functionality of the TPoolAllocator class, which
// is documented in PoolAlloc.h. // is documented in PoolAlloc.h.
// //
TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) : TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment)
pageSize(growthIncrement), : alignment(allocationAlignment),
alignment(allocationAlignment), #if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
freeList(0), pageSize(growthIncrement),
inUseList(0), freeList(0),
numCalls(0), inUseList(0),
totalBytes(0), numCalls(0),
mLocked(false) totalBytes(0),
#endif
mLocked(false)
{ {
// //
// Don't allow page sizes we know are smaller than all common
// OS page sizes.
//
if (pageSize < 4*1024)
pageSize = 4*1024;
//
// A large currentPageOffset indicates a new page needs to
// be obtained to allocate memory.
//
currentPageOffset = pageSize;
//
// Adjust alignment to be at least pointer aligned and // Adjust alignment to be at least pointer aligned and
// power of 2. // power of 2.
// //
...@@ -86,6 +75,20 @@ TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) : ...@@ -86,6 +75,20 @@ TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
alignment = a; alignment = a;
alignmentMask = a - 1; alignmentMask = a - 1;
#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
//
// Don't allow page sizes we know are smaller than all common
// OS page sizes.
//
if (pageSize < 4 * 1024)
pageSize = 4 * 1024;
//
// A large currentPageOffset indicates a new page needs to
// be obtained to allocate memory.
//
currentPageOffset = pageSize;
// //
// Align header skip // Align header skip
// //
...@@ -93,10 +96,14 @@ TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) : ...@@ -93,10 +96,14 @@ TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
if (headerSkip < sizeof(tHeader)) { if (headerSkip < sizeof(tHeader)) {
headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask; headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
} }
#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
mStack.push_back({});
#endif
} }
TPoolAllocator::~TPoolAllocator() TPoolAllocator::~TPoolAllocator()
{ {
#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
while (inUseList) { while (inUseList) {
tHeader* next = inUseList->nextPage; tHeader* next = inUseList->nextPage;
inUseList->~tHeader(); inUseList->~tHeader();
...@@ -113,6 +120,16 @@ TPoolAllocator::~TPoolAllocator() ...@@ -113,6 +120,16 @@ TPoolAllocator::~TPoolAllocator()
delete [] reinterpret_cast<char*>(freeList); delete [] reinterpret_cast<char*>(freeList);
freeList = next; freeList = next;
} }
#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
for (auto &allocs : mStack)
{
for (auto alloc : allocs)
{
free(alloc);
}
}
mStack.clear();
#endif
} }
// Support MSVC++ 6.0 // Support MSVC++ 6.0
...@@ -153,14 +170,18 @@ void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, co ...@@ -153,14 +170,18 @@ void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, co
void TPoolAllocator::push() void TPoolAllocator::push()
{ {
#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
tAllocState state = { currentPageOffset, inUseList }; tAllocState state = { currentPageOffset, inUseList };
stack.push_back(state); mStack.push_back(state);
// //
// Indicate there is no current page to allocate from. // Indicate there is no current page to allocate from.
// //
currentPageOffset = pageSize; currentPageOffset = pageSize;
#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
mStack.push_back({});
#endif
} }
// //
...@@ -172,11 +193,12 @@ void TPoolAllocator::push() ...@@ -172,11 +193,12 @@ void TPoolAllocator::push()
// //
void TPoolAllocator::pop() void TPoolAllocator::pop()
{ {
if (stack.size() < 1) if (mStack.size() < 1)
return; return;
tHeader* page = stack.back().page; #if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
currentPageOffset = stack.back().offset; tHeader *page = mStack.back().page;
currentPageOffset = mStack.back().offset;
while (inUseList != page) { while (inUseList != page) {
// invoke destructor to free allocation list // invoke destructor to free allocation list
...@@ -192,7 +214,14 @@ void TPoolAllocator::pop() ...@@ -192,7 +214,14 @@ void TPoolAllocator::pop()
inUseList = nextInUse; inUseList = nextInUse;
} }
stack.pop_back(); mStack.pop_back();
#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
for (auto &alloc : mStack.back())
{
free(alloc);
}
mStack.pop_back();
#endif
} }
// //
...@@ -201,7 +230,7 @@ void TPoolAllocator::pop() ...@@ -201,7 +230,7 @@ void TPoolAllocator::pop()
// //
void TPoolAllocator::popAll() void TPoolAllocator::popAll()
{ {
while (stack.size() > 0) while (mStack.size() > 0)
pop(); pop();
} }
...@@ -209,6 +238,7 @@ void* TPoolAllocator::allocate(size_t numBytes) ...@@ -209,6 +238,7 @@ void* TPoolAllocator::allocate(size_t numBytes)
{ {
ASSERT(!mLocked); ASSERT(!mLocked);
#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
// //
// Just keep some interesting statistics. // Just keep some interesting statistics.
// //
...@@ -285,6 +315,14 @@ void* TPoolAllocator::allocate(size_t numBytes) ...@@ -285,6 +315,14 @@ void* TPoolAllocator::allocate(size_t numBytes)
currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask; currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
return initializeAllocation(inUseList, ret, numBytes); return initializeAllocation(inUseList, ret, numBytes);
#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
void *alloc = malloc(numBytes + alignmentMask);
mStack.back().push_back(alloc);
intptr_t intAlloc = reinterpret_cast<intptr_t>(alloc);
intAlloc = (intAlloc + alignmentMask) & ~alignmentMask;
return reinterpret_cast<void *>(intAlloc);
#endif
} }
void TPoolAllocator::lock() void TPoolAllocator::lock()
......
...@@ -157,7 +157,12 @@ public: ...@@ -157,7 +157,12 @@ public:
void lock(); void lock();
void unlock(); void unlock();
protected: private:
size_t alignment; // all returned allocations will be aligned at
// this granularity, which will be a power of 2
size_t alignmentMask;
#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
friend struct tHeader; friend struct tHeader;
struct tHeader { struct tHeader {
...@@ -200,20 +205,21 @@ protected: ...@@ -200,20 +205,21 @@ protected:
} }
size_t pageSize; // granularity of allocation from the OS size_t pageSize; // granularity of allocation from the OS
size_t alignment; // all returned allocations will be aligned at
// this granularity, which will be a power of 2
size_t alignmentMask;
size_t headerSkip; // amount of memory to skip to make room for the size_t headerSkip; // amount of memory to skip to make room for the
// header (basically, size of header, rounded // header (basically, size of header, rounded
// up to make it aligned // up to make it aligned
size_t currentPageOffset; // next offset in top of inUseList to allocate from size_t currentPageOffset; // next offset in top of inUseList to allocate from
tHeader* freeList; // list of popped memory tHeader* freeList; // list of popped memory
tHeader* inUseList; // list of all memory currently being used tHeader* inUseList; // list of all memory currently being used
tAllocStack stack; // stack of where to allocate from, to partition pool tAllocStack mStack; // stack of where to allocate from, to partition pool
int numCalls; // just an interesting statistic int numCalls; // just an interesting statistic
size_t totalBytes; // just an interesting statistic size_t totalBytes; // just an interesting statistic
private:
#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
std::vector<std::vector<void *>> mStack;
#endif
TPoolAllocator& operator=(const TPoolAllocator&); // dont allow assignment operator TPoolAllocator& operator=(const TPoolAllocator&); // dont allow assignment operator
TPoolAllocator(const TPoolAllocator&); // dont allow default copy constructor TPoolAllocator(const TPoolAllocator&); // dont allow default copy constructor
bool mLocked; bool mLocked;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment