SPIRV-Tools/source/fuzz/protobufs/spvtoolsfuzz.proto
Alastair Donaldson 001e823b65
Add fuzzer pass to obfuscate constants. (#2671)
Adds a new transformation that can replace a constant with a uniform known to have the same value, and adds a fuzzer pass that (a) replaces a boolean with a comparison of literals (e.g. replacing "true" with "42 > 24"), and then (b) obfuscates the literals appearing in this comparison by replacing them with identically-valued uniforms, if available.

The fuzzer_replayer test file has also been updated to allow initial facts to be provided, and to do error checking of the status results returned by the fuzzer and replayer components.
2019-06-18 18:41:08 +01:00

314 lines
9.3 KiB
Protocol Buffer

// Copyright (c) 2019 Google LLC
//
// 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.
// This file is specifically named spvtools_fuzz.proto so that the string
// 'spvtools_fuzz' appears in the names of global-scope symbols that protoc
// generates when targeting C++. This is to reduce the potential for name
// clashes with other globally-scoped symbols.
syntax = "proto3";
package spvtools.fuzz.protobufs;
message IdUseDescriptor {
// Describes a use of an id as an input operand to an instruction in some block
// of a function.
// Example:
// - id_of_interest = 42
// - target_instruction_opcode = OpStore
// - in_operand_index = 1
// - base_instruction_result_id = 50
// - num_opcodes_to_ignore = 7
// represents a use of id 42 as input operand 1 to an OpStore instruction,
// such that the OpStore instruction can be found in the same basic block as
// the instruction with result id 50, and in particular is the 8th OpStore
// instruction found from instruction 50 onwards (i.e. 7 OpStore
// instructions are skipped).
// An id that we would like to be able to find a use of.
uint32 id_of_interest = 1;
// The opcode for the instruction that uses the id.
uint32 target_instruction_opcode = 2;
// The input operand index at which the use is expected.
uint32 in_operand_index = 3;
// The id of an instruction after which the instruction that contains the use
// is believed to occur; it might be the using instruction itself.
uint32 base_instruction_result_id = 4;
// The number of matching opcodes to skip over when searching for the using
// instruction from the base instruction.
uint32 num_opcodes_to_ignore = 5;
}
message UniformBufferElementDescriptor {
// Represents a data element inside a uniform buffer. The element is
// specified via (a) the result id of a uniform variable in which the element
// is contained, and (b) a series of indices that need to be followed to get
// to the element (via fields and array/vector indices).
//
// Example: suppose %42 is the id of a uniform variable, and that the uniform
// variable has the following type (using GLSL-like syntax):
//
// struct S {
// float f;
// vec3 g;
// int4 h[10];
// };
//
// Then:
// - 42[0] describes the 'f' field.
// - 42[1,1] describes the y component of the 'g' field.
// - 42[2,7,3] describes the w component of element 7 of the 'h' field
// The result id of a uniform variable.
uint32 uniform_variable_id = 1;
// An ordered sequence of indices through composite structures in the
// uniform buffer.
repeated uint32 index = 2;
}
message FactSequence {
repeated Fact fact = 1;
}
message Fact {
oneof fact {
// Order the fact options by numeric id (rather than alphabetically).
FactConstantUniform constant_uniform_fact = 1;
}
}
// Keep fact message types in alphabetical order:
message FactConstantUniform {
// Records the fact that a uniform buffer element is guaranteed to be equal
// to a particular constant value. spirv-fuzz can use such guarantees to
// obfuscate code, e.g. to manufacture an expression that will (due to the
// guarantee) evaluate to a particular value at runtime but in a manner that
// cannot be predicted at compile-time.
// An element of a uniform buffer
UniformBufferElementDescriptor uniform_buffer_element_descriptor = 1;
// The words of the associated constant
repeated uint32 constant_word = 2;
}
message TransformationSequence {
repeated Transformation transformation = 1;
}
message Transformation {
oneof transformation {
// Order the transformation options by numeric id (rather than
// alphabetically).
TransformationMoveBlockDown move_block_down = 1;
TransformationSplitBlock split_block = 2;
TransformationAddConstantBoolean add_constant_boolean = 3;
TransformationAddConstantScalar add_constant_scalar = 4;
TransformationAddTypeBoolean add_type_boolean = 5;
TransformationAddTypeFloat add_type_float = 6;
TransformationAddTypeInt add_type_int = 7;
TransformationAddDeadBreak add_dead_break = 8;
TransformationReplaceBooleanConstantWithConstantBinary replace_boolean_constant_with_constant_binary = 9;
TransformationAddTypePointer add_type_pointer = 10;
TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11;
// Add additional option using the next available number.
}
}
// Keep transformation message types in alphabetical order:
message TransformationAddConstantBoolean {
// Supports adding the constants true and false to a module, which may be
// necessary in order to enable other transformations if they are not present.
uint32 fresh_id = 1;
bool is_true = 2;
}
message TransformationAddConstantScalar {
// Adds a constant of the given scalar type
// Id for the constant
uint32 fresh_id = 1;
// Id for the scalar type of the constant
uint32 type_id = 2;
// Value of the constant
repeated uint32 word = 3;
}
message TransformationAddDeadBreak {
// A transformation that turns a basic block that unconditionally branches to
// its successor into a block that potentially breaks out of a structured
// control flow construct, but in such a manner that the break cannot actually
// be taken.
// The block to break from
uint32 from_block = 1;
// The merge block to break to
uint32 to_block = 2;
// Determines whether the break condition is true or false
bool break_condition_value = 3;
// A sequence of ids suitable for extending OpPhi instructions as a result of
// the new break edge
repeated uint32 phi_id = 4;
}
message TransformationAddTypeBoolean {
// Adds OpTypeBool to the module
// Id to be used for the type
uint32 fresh_id = 1;
}
message TransformationAddTypeFloat {
// Adds OpTypeFloat to the module with the given width
// Id to be used for the type
uint32 fresh_id = 1;
// Floating-point width
uint32 width = 2;
}
message TransformationAddTypeInt {
// Adds OpTypeInt to the module with the given width and signedness
// Id to be used for the type
uint32 fresh_id = 1;
// Integer width
uint32 width = 2;
// True if and only if this is a signed type
bool is_signed = 3;
}
message TransformationAddTypePointer {
// Adds OpTypePointer to the module, with the given storage class and base
// type
// Id to be used for the type
uint32 fresh_id = 1;
// Pointer storage class
uint32 storage_class = 2;
// Id of the base type for the pointer
uint32 base_type_id = 3;
}
message TransformationMoveBlockDown {
// A transformation that moves a basic block to be one position lower in
// program order.
// The id of the block to move down.
uint32 block_id = 1;
}
message TransformationReplaceConstantWithUniform {
// Replaces a use of a constant id with the the result of a load from an
// element of uniform buffer known to hold the same value as the constant
// A descriptor for the id we would like to replace
IdUseDescriptor id_use_descriptor = 1;
// Uniform descriptor to identify which uniform value to choose
UniformBufferElementDescriptor uniform_descriptor = 2;
// Id that will store the result of an access chain
uint32 fresh_id_for_access_chain = 3;
// Id that will store the result of a load
uint32 fresh_id_for_load = 4;
}
message TransformationReplaceBooleanConstantWithConstantBinary {
// A transformation to capture replacing a use of a boolean constant with
// binary operation on two constant values
// A descriptor for the boolean constant id we would like to replace
IdUseDescriptor id_use_descriptor = 1;
// Id for the constant to be used on the LHS of the comparision
uint32 lhs_id = 2;
// Id for the constant to be used on the RHS of the comparision
uint32 rhs_id = 3;
// Opcode for binary operator
uint32 opcode = 4;
// Id that will store the result of the binary operation instruction
uint32 fresh_id_for_binary_operation = 5;
}
message TransformationSplitBlock {
// A transformation that splits a basic block into two basic blocks.
// The result id of an instruction.
uint32 result_id = 1;
// An offset, such that the block containing |result_id_| should be split
// right before the instruction |offset_| instructions after |result_id_|.
uint32 offset = 2;
// An id that must not yet be used by the module to which this transformation
// is applied. Rather than having the transformation choose a suitable id on
// application, we require the id to be given upfront in order to facilitate
// reducing fuzzed shaders by removing transformations. The reason is that
// future transformations may refer to the fresh id introduced by this
// transformation, and if we end up changing what that id is, due to removing
// earlier transformations, it may inhibit later transformations from
// applying.
uint32 fresh_id = 3;
}