• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "source/fuzz/transformation_store.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19 
20 namespace spvtools {
21 namespace fuzz {
22 
TransformationStore(protobufs::TransformationStore message)23 TransformationStore::TransformationStore(protobufs::TransformationStore message)
24     : message_(std::move(message)) {}
25 
TransformationStore(uint32_t pointer_id,uint32_t value_id,const protobufs::InstructionDescriptor & instruction_to_insert_before)26 TransformationStore::TransformationStore(
27     uint32_t pointer_id, uint32_t value_id,
28     const protobufs::InstructionDescriptor& instruction_to_insert_before) {
29   message_.set_pointer_id(pointer_id);
30   message_.set_value_id(value_id);
31   *message_.mutable_instruction_to_insert_before() =
32       instruction_to_insert_before;
33 }
34 
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const35 bool TransformationStore::IsApplicable(
36     opt::IRContext* ir_context,
37     const TransformationContext& transformation_context) const {
38   // The pointer must exist and have a type.
39   auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
40   if (!pointer || !pointer->type_id()) {
41     return false;
42   }
43 
44   // The pointer type must indeed be a pointer.
45   auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
46   assert(pointer_type && "Type id must be defined.");
47   if (pointer_type->opcode() != SpvOpTypePointer) {
48     return false;
49   }
50 
51   // The pointer must not be read only.
52   if (pointer->IsReadOnlyPointer()) {
53     return false;
54   }
55 
56   // We do not want to allow storing to null or undefined pointers.
57   switch (pointer->opcode()) {
58     case SpvOpConstantNull:
59     case SpvOpUndef:
60       return false;
61     default:
62       break;
63   }
64 
65   // Determine which instruction we should be inserting before.
66   auto insert_before =
67       FindInstruction(message_.instruction_to_insert_before(), ir_context);
68   // It must exist, ...
69   if (!insert_before) {
70     return false;
71   }
72   // ... and it must be legitimate to insert a store before it.
73   if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
74                                                     insert_before)) {
75     return false;
76   }
77 
78   // The block we are inserting into needs to be dead, or else the pointee type
79   // of the pointer we are storing to needs to be irrelevant (otherwise the
80   // store could impact on the observable behaviour of the module).
81   if (!transformation_context.GetFactManager()->BlockIsDead(
82           ir_context->get_instr_block(insert_before)->id()) &&
83       !transformation_context.GetFactManager()->PointeeValueIsIrrelevant(
84           message_.pointer_id())) {
85     return false;
86   }
87 
88   // The value being stored needs to exist and have a type.
89   auto value = ir_context->get_def_use_mgr()->GetDef(message_.value_id());
90   if (!value || !value->type_id()) {
91     return false;
92   }
93 
94   // The type of the value must match the pointee type.
95   if (pointer_type->GetSingleWordInOperand(1) != value->type_id()) {
96     return false;
97   }
98 
99   // The pointer needs to be available at the insertion point.
100   if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
101                                                   message_.pointer_id())) {
102     return false;
103   }
104 
105   // The value needs to be available at the insertion point.
106   return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
107                                                     message_.value_id());
108 }
109 
Apply(opt::IRContext * ir_context,TransformationContext *) const110 void TransformationStore::Apply(opt::IRContext* ir_context,
111                                 TransformationContext* /*unused*/) const {
112   auto insert_before =
113       FindInstruction(message_.instruction_to_insert_before(), ir_context);
114   auto new_instruction = MakeUnique<opt::Instruction>(
115       ir_context, SpvOpStore, 0, 0,
116       opt::Instruction::OperandList(
117           {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
118            {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
119   auto new_instruction_ptr = new_instruction.get();
120   insert_before->InsertBefore(std::move(new_instruction));
121   // Inform the def-use manager about the new instruction and record its basic
122   // block.
123   ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
124   ir_context->set_instr_block(new_instruction_ptr,
125                               ir_context->get_instr_block(insert_before));
126 }
127 
ToMessage() const128 protobufs::Transformation TransformationStore::ToMessage() const {
129   protobufs::Transformation result;
130   *result.mutable_store() = message_;
131   return result;
132 }
133 
GetFreshIds() const134 std::unordered_set<uint32_t> TransformationStore::GetFreshIds() const {
135   return std::unordered_set<uint32_t>();
136 }
137 
138 }  // namespace fuzz
139 }  // namespace spvtools
140