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/fuzzer_pass_add_stores.h"
16 
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/transformation_store.h"
19 
20 namespace spvtools {
21 namespace fuzz {
22 
FuzzerPassAddStores( opt::IRContext* ir_context, TransformationContext* transformation_context, FuzzerContext* fuzzer_context, protobufs::TransformationSequence* transformations, bool ignore_inapplicable_transformations)23 FuzzerPassAddStores::FuzzerPassAddStores(
24     opt::IRContext* ir_context, TransformationContext* transformation_context,
25     FuzzerContext* fuzzer_context,
26     protobufs::TransformationSequence* transformations,
27     bool ignore_inapplicable_transformations)
28     : FuzzerPass(ir_context, transformation_context, fuzzer_context,
29                  transformations, ignore_inapplicable_transformations) {}
30 
Apply()31 void FuzzerPassAddStores::Apply() {
32   ForEachInstructionWithInstructionDescriptor(
33       [this](opt::Function* function, opt::BasicBlock* block,
34              opt::BasicBlock::iterator inst_it,
35              const protobufs::InstructionDescriptor& instruction_descriptor)
36           -> void {
37         assert(
38             inst_it->opcode() ==
39                 spv::Op(instruction_descriptor.target_instruction_opcode()) &&
40             "The opcode of the instruction we might insert before must be "
41             "the same as the opcode in the descriptor for the instruction");
42 
43         // Randomly decide whether to try inserting a store here.
44         if (!GetFuzzerContext()->ChoosePercentage(
45                 GetFuzzerContext()->GetChanceOfAddingStore())) {
46           return;
47         }
48 
49         // Check whether it is legitimate to insert a store before this
50         // instruction.
51         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpStore,
52                                                           inst_it)) {
53           return;
54         }
55         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
56                 spv::Op::OpAtomicStore, inst_it)) {
57           return;
58         }
59 
60         // Look for pointers we might consider storing to.
61         std::vector<opt::Instruction*> relevant_pointers =
62             FindAvailableInstructions(
63                 function, block, inst_it,
64                 [this, block](opt::IRContext* context,
65                               opt::Instruction* instruction) -> bool {
66                   if (!instruction->result_id() || !instruction->type_id()) {
67                     return false;
68                   }
69                   auto type_inst = context->get_def_use_mgr()->GetDef(
70                       instruction->type_id());
71                   if (type_inst->opcode() != spv::Op::OpTypePointer) {
72                     // Not a pointer.
73                     return false;
74                   }
75                   if (instruction->IsReadOnlyPointer()) {
76                     // Read only: cannot store to it.
77                     return false;
78                   }
79                   switch (instruction->opcode()) {
80                     case spv::Op::OpConstantNull:
81                     case spv::Op::OpUndef:
82                       // Do not allow storing to a null or undefined pointer;
83                       // this might be OK if the block is dead, but for now we
84                       // conservatively avoid it.
85                       return false;
86                     default:
87                       break;
88                   }
89                   return GetTransformationContext()
90                              ->GetFactManager()
91                              ->BlockIsDead(block->id()) ||
92                          GetTransformationContext()
93                              ->GetFactManager()
94                              ->PointeeValueIsIrrelevant(
95                                  instruction->result_id());
96                 });
97 
98         // At this point, |relevant_pointers| contains all the pointers we might
99         // think of storing to.
100         if (relevant_pointers.empty()) {
101           return;
102         }
103 
104         auto pointer = relevant_pointers[GetFuzzerContext()->RandomIndex(
105             relevant_pointers)];
106 
107         std::vector<opt::Instruction*> relevant_values =
108             FindAvailableInstructions(
109                 function, block, inst_it,
110                 [pointer](opt::IRContext* context,
111                           opt::Instruction* instruction) -> bool {
112                   if (!instruction->result_id() || !instruction->type_id()) {
113                     return false;
114                   }
115                   return instruction->type_id() ==
116                          context->get_def_use_mgr()
117                              ->GetDef(pointer->type_id())
118                              ->GetSingleWordInOperand(1);
119                 });
120 
121         if (relevant_values.empty()) {
122           return;
123         }
124 
125         bool is_atomic_store = false;
126         uint32_t memory_scope_id = 0;
127         uint32_t memory_semantics_id = 0;
128 
129         auto storage_class =
130             static_cast<spv::StorageClass>(GetIRContext()
131                                                ->get_def_use_mgr()
132                                                ->GetDef(pointer->type_id())
133                                                ->GetSingleWordInOperand(0));
134 
135         switch (storage_class) {
136           case spv::StorageClass::StorageBuffer:
137           case spv::StorageClass::PhysicalStorageBuffer:
138           case spv::StorageClass::Workgroup:
139           case spv::StorageClass::CrossWorkgroup:
140           case spv::StorageClass::AtomicCounter:
141           case spv::StorageClass::Image:
142             if (GetFuzzerContext()->ChoosePercentage(
143                     GetFuzzerContext()->GetChanceOfAddingAtomicStore())) {
144               is_atomic_store = true;
145 
146               memory_scope_id = FindOrCreateConstant(
147                   {uint32_t(spv::Scope::Invocation)},
148                   FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()),
149                   false);
150 
151               memory_semantics_id = FindOrCreateConstant(
152                   {static_cast<uint32_t>(
153                       fuzzerutil::GetMemorySemanticsForStorageClass(
154                           storage_class))},
155                   FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()),
156                   false);
157             }
158             break;
159 
160           default:
161             break;
162         }
163 
164         // Create and apply the transformation.
165         ApplyTransformation(TransformationStore(
166             pointer->result_id(), is_atomic_store, memory_scope_id,
167             memory_semantics_id,
168             relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)]
169                 ->result_id(),
170             instruction_descriptor));
171       });
172 }
173 
174 }  // namespace fuzz
175 }  // namespace spvtools
176