| // 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 <cstdint> | 
 | #include <memory> | 
 | #include <cassert> | 
 |  | 
 | 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<ScopedPoolAllocatorAndTLS> allocatorAndTLS(new ScopedPoolAllocatorAndTLS); | 
 | 	std::unique_ptr<sw::VertexShader> shader(new sw::VertexShader); | 
 | 	std::unique_ptr<FakeVS> fakeVS(new FakeVS(shader.get())); | 
 |  | 
 | 	std::unique_ptr<TranslatorASM> 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<const char*>(data + kHeaderSize); | 
 | 	if (!glslCompiler->compile(&glslSource, 1, SH_OBJECT_CODE)) | 
 | 	{ | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	std::unique_ptr<sw::VertexShader> 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(); | 
 |  | 
 | 	sw::Routine *routine = program("VertexRoutine"); | 
 | 	assert(routine); | 
 | 	const void *entry = routine->getEntry(); | 
 | 	assert(entry); (void)entry; | 
 | 	delete routine; | 
 |  | 
 | 	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 |