// Copyright 2017 The SwiftShader Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "OpenGL/compiler/InitializeGlobals.h" #include "OpenGL/compiler/InitializeParseContext.h" #include "OpenGL/compiler/TranslatorASM.h" // TODO: Debug macros of the GLSL compiler clash with core SwiftShader's. // They should not be exposed through the interface headers above. #undef ASSERT #undef UNIMPLEMENTED #include "Renderer/VertexProcessor.hpp" #include "Shader/VertexProgram.hpp" #include #include #include namespace { // TODO(cwallez@google.com): Like in ANGLE, disable most of the pool allocator for fuzzing // This is a helper class to make sure all the resources used by the compiler are initialized class ScopedPoolAllocatorAndTLS { public: ScopedPoolAllocatorAndTLS() { InitializeParseContextIndex(); InitializePoolIndex(); SetGlobalPoolAllocator(&allocator); } ~ScopedPoolAllocatorAndTLS() { SetGlobalPoolAllocator(nullptr); FreePoolIndex(); FreeParseContextIndex(); } private: TPoolAllocator allocator; }; // Trivial implementation of the glsl::Shader interface that fakes being an API-level // shader object. class FakeVS : public glsl::Shader { public: FakeVS(sw::VertexShader *bytecode) : bytecode(bytecode) { } sw::Shader *getShader() const override { return bytecode; } sw::VertexShader *getVertexShader() const override { return bytecode; } private: sw::VertexShader *bytecode; }; } // anonymous namespace extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { // Data layout: // // byte: boolean states // { // byte: stream type // byte: stream count and normalized // } [MAX_VERTEX_INPUTS] // { // byte[32]: reserved sampler state // } [VERTEX_TEXTURE_IMAGE_UNITS] // // char source[] // null terminated const size_t kHeaderSize = 1 + 2 * sw::MAX_VERTEX_INPUTS + 32 * sw::VERTEX_TEXTURE_IMAGE_UNITS; if(size <= kHeaderSize) { return 0; } if(data[size - 1] != 0) { return 0; } std::unique_ptr allocatorAndTLS(new ScopedPoolAllocatorAndTLS); std::unique_ptr shader(new sw::VertexShader); std::unique_ptr fakeVS(new FakeVS(shader.get())); std::unique_ptr glslCompiler(new TranslatorASM(fakeVS.get(), GL_VERTEX_SHADER)); // TODO(cwallez@google.com): have a function to init to default values somewhere ShBuiltInResources resources; resources.MaxVertexAttribs = sw::MAX_VERTEX_INPUTS; resources.MaxVertexUniformVectors = sw::VERTEX_UNIFORM_VECTORS - 3; resources.MaxVaryingVectors = MIN(sw::MAX_VERTEX_OUTPUTS, sw::MAX_VERTEX_INPUTS); resources.MaxVertexTextureImageUnits = sw::VERTEX_TEXTURE_IMAGE_UNITS; resources.MaxCombinedTextureImageUnits = sw::TEXTURE_IMAGE_UNITS + sw::VERTEX_TEXTURE_IMAGE_UNITS; resources.MaxTextureImageUnits = sw::TEXTURE_IMAGE_UNITS; resources.MaxFragmentUniformVectors = sw::FRAGMENT_UNIFORM_VECTORS - 3; resources.MaxDrawBuffers = sw::RENDERTARGETS; resources.MaxVertexOutputVectors = 16; // ??? resources.MaxFragmentInputVectors = 15; // ??? resources.MinProgramTexelOffset = sw::MIN_PROGRAM_TEXEL_OFFSET; resources.MaxProgramTexelOffset = sw::MAX_PROGRAM_TEXEL_OFFSET; resources.OES_standard_derivatives = 1; resources.OES_fragment_precision_high = 1; resources.OES_EGL_image_external = 1; resources.OES_EGL_image_external_essl3 = 1; resources.EXT_draw_buffers = 1; resources.ARB_texture_rectangle = 1; resources.MaxCallStackDepth = 16; glslCompiler->Init(resources); const char *glslSource = reinterpret_cast(data + kHeaderSize); if(!glslCompiler->compile(&glslSource, 1, SH_OBJECT_CODE)) { return 0; } std::unique_ptr bytecodeShader(new sw::VertexShader(fakeVS->getVertexShader())); sw::VertexProcessor::State state; state.textureSampling = bytecodeShader->containsTextureSampling(); state.positionRegister = bytecodeShader->getPositionRegister(); state.pointSizeRegister = bytecodeShader->getPointSizeRegister(); state.preTransformed = (data[0] & 0x01) != 0; state.superSampling = (data[0] & 0x02) != 0; state.transformFeedbackQueryEnabled = (data[0] & 0x08) != 0; state.transformFeedbackEnabled = (data[0] & 0x10) != 0; state.verticesPerPrimitive = 1 + ((data[0] & 0x20) != 0) + ((data[0] & 0x40) != 0); if((data[0] & 0x80) != 0) // Unused/reserved. { return 0; } constexpr int MAX_ATTRIBUTE_COMPONENTS = 4; struct Stream { uint8_t count : BITS(MAX_ATTRIBUTE_COMPONENTS); bool normalized : 1; uint8_t reserved : 8 - BITS(MAX_ATTRIBUTE_COMPONENTS) - 1; }; for(int i = 0; i < sw::MAX_VERTEX_INPUTS; i++) { sw::StreamType type = (sw::StreamType)data[1 + 2 * i + 0]; Stream stream = (Stream &)data[1 + 2 * i + 1]; if(type > sw::STREAMTYPE_LAST) return 0; if(stream.count > MAX_ATTRIBUTE_COMPONENTS) return 0; if(stream.reserved != 0) return 0; state.input[i].type = type; state.input[i].count = stream.count; state.input[i].normalized = stream.normalized; state.input[i].attribType = bytecodeShader->getAttribType(i); } for(unsigned int i = 0; i < sw::VERTEX_TEXTURE_IMAGE_UNITS; i++) { // TODO // if(bytecodeShader->usesSampler(i)) // { // state.samplerState[i] = context->sampler[sw::TEXTURE_IMAGE_UNITS + i].samplerState(); // } for(int j = 0; j < 32; j++) { if(data[1 + 2 * sw::MAX_VERTEX_INPUTS + 32 * i + j] != 0) { return 0; } } } for(int i = 0; i < sw::MAX_VERTEX_OUTPUTS; i++) { state.output[i].xWrite = bytecodeShader->getOutput(i, 0).active(); state.output[i].yWrite = bytecodeShader->getOutput(i, 1).active(); state.output[i].zWrite = bytecodeShader->getOutput(i, 2).active(); state.output[i].wWrite = bytecodeShader->getOutput(i, 3).active(); } sw::VertexProgram program(state, bytecodeShader.get()); program.generate(); auto routine = program("VertexRoutine"); assert(routine); const void *entry = routine->getEntry(); assert(entry); (void)entry; return 0; } #if defined(FUZZER_STANDALONE_REPRODUCE) int main(int argc, char *argv[]) { FILE *file = fopen("clusterfuzz-testcase", "r"); fseek(file, 0L, SEEK_END); long numbytes = ftell(file); fseek(file, 0L, SEEK_SET); uint8_t *buffer = (uint8_t *)calloc(numbytes, sizeof(uint8_t)); fread(buffer, sizeof(char), numbytes, file); fclose(file); while(true) { LLVMFuzzerTestOneInput(buffer, numbytes); } free(buffer); return 0; } #endif