| // Copyright (c) 2020 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. | 
 |  | 
 | #include "source/fuzz/fuzzer_pass_add_stores.h" | 
 |  | 
 | #include "source/fuzz/fuzzer_util.h" | 
 | #include "source/fuzz/transformation_store.h" | 
 |  | 
 | namespace spvtools { | 
 | namespace fuzz { | 
 |  | 
 | FuzzerPassAddStores::FuzzerPassAddStores( | 
 |     opt::IRContext* ir_context, FactManager* fact_manager, | 
 |     FuzzerContext* fuzzer_context, | 
 |     protobufs::TransformationSequence* transformations) | 
 |     : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {} | 
 |  | 
 | FuzzerPassAddStores::~FuzzerPassAddStores() = default; | 
 |  | 
 | void FuzzerPassAddStores::Apply() { | 
 |   MaybeAddTransformationBeforeEachInstruction( | 
 |       [this](opt::Function* function, opt::BasicBlock* block, | 
 |              opt::BasicBlock::iterator inst_it, | 
 |              const protobufs::InstructionDescriptor& instruction_descriptor) | 
 |           -> void { | 
 |         assert(inst_it->opcode() == | 
 |                    instruction_descriptor.target_instruction_opcode() && | 
 |                "The opcode of the instruction we might insert before must be " | 
 |                "the same as the opcode in the descriptor for the instruction"); | 
 |  | 
 |         // Check whether it is legitimate to insert a store before this | 
 |         // instruction. | 
 |         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, | 
 |                                                           inst_it)) { | 
 |           return; | 
 |         } | 
 |  | 
 |         // Randomly decide whether to try inserting a store here. | 
 |         if (!GetFuzzerContext()->ChoosePercentage( | 
 |                 GetFuzzerContext()->GetChanceOfAddingStore())) { | 
 |           return; | 
 |         } | 
 |  | 
 |         // Look for pointers we might consider storing to. | 
 |         std::vector<opt::Instruction*> relevant_pointers = | 
 |             FindAvailableInstructions( | 
 |                 function, block, inst_it, | 
 |                 [this, block](opt::IRContext* context, | 
 |                               opt::Instruction* instruction) -> bool { | 
 |                   if (!instruction->result_id() || !instruction->type_id()) { | 
 |                     return false; | 
 |                   } | 
 |                   auto type_inst = context->get_def_use_mgr()->GetDef( | 
 |                       instruction->type_id()); | 
 |                   if (type_inst->opcode() != SpvOpTypePointer) { | 
 |                     // Not a pointer. | 
 |                     return false; | 
 |                   } | 
 |                   if (type_inst->GetSingleWordInOperand(0) == | 
 |                       SpvStorageClassInput) { | 
 |                     // Read-only: cannot store to it. | 
 |                     return false; | 
 |                   } | 
 |                   switch (instruction->result_id()) { | 
 |                     case SpvOpConstantNull: | 
 |                     case SpvOpUndef: | 
 |                       // Do not allow storing to a null or undefined pointer; | 
 |                       // this might be OK if the block is dead, but for now we | 
 |                       // conservatively avoid it. | 
 |                       return false; | 
 |                     default: | 
 |                       break; | 
 |                   } | 
 |                   return GetFactManager()->BlockIsDead(block->id()) || | 
 |                          GetFactManager()->PointeeValueIsIrrelevant( | 
 |                              instruction->result_id()); | 
 |                 }); | 
 |  | 
 |         // At this point, |relevant_pointers| contains all the pointers we might | 
 |         // think of storing to. | 
 |         if (relevant_pointers.empty()) { | 
 |           return; | 
 |         } | 
 |  | 
 |         auto pointer = relevant_pointers[GetFuzzerContext()->RandomIndex( | 
 |             relevant_pointers)]; | 
 |  | 
 |         std::vector<opt::Instruction*> relevant_values = | 
 |             FindAvailableInstructions( | 
 |                 function, block, inst_it, | 
 |                 [pointer](opt::IRContext* context, | 
 |                           opt::Instruction* instruction) -> bool { | 
 |                   if (!instruction->result_id() || !instruction->type_id()) { | 
 |                     return false; | 
 |                   } | 
 |                   return instruction->type_id() == | 
 |                          context->get_def_use_mgr() | 
 |                              ->GetDef(pointer->type_id()) | 
 |                              ->GetSingleWordInOperand(1); | 
 |                 }); | 
 |  | 
 |         if (relevant_values.empty()) { | 
 |           return; | 
 |         } | 
 |  | 
 |         // Choose a value at random, and create and apply a storing | 
 |         // transformation based on it and the pointer. | 
 |         ApplyTransformation(TransformationStore( | 
 |             pointer->result_id(), | 
 |             relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)] | 
 |                 ->result_id(), | 
 |             instruction_descriptor)); | 
 |       }); | 
 | } | 
 |  | 
 | }  // namespace fuzz | 
 | }  // namespace spvtools |