• 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/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(inst_it->opcode() ==
38                    instruction_descriptor.target_instruction_opcode() &&
39                "The opcode of the instruction we might insert before must be "
40                "the same as the opcode in the descriptor for the instruction");
41 
42         // Randomly decide whether to try inserting a store here.
43         if (!GetFuzzerContext()->ChoosePercentage(
44                 GetFuzzerContext()->GetChanceOfAddingStore())) {
45           return;
46         }
47 
48         // Check whether it is legitimate to insert a store before this
49         // instruction.
50         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore,
51                                                           inst_it)) {
52           return;
53         }
54         if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAtomicStore,
55                                                           inst_it)) {
56           return;
57         }
58 
59         // Look for pointers we might consider storing to.
60         std::vector<opt::Instruction*> relevant_pointers =
61             FindAvailableInstructions(
62                 function, block, inst_it,
63                 [this, block](opt::IRContext* context,
64                               opt::Instruction* instruction) -> bool {
65                   if (!instruction->result_id() || !instruction->type_id()) {
66                     return false;
67                   }
68                   auto type_inst = context->get_def_use_mgr()->GetDef(
69                       instruction->type_id());
70                   if (type_inst->opcode() != SpvOpTypePointer) {
71                     // Not a pointer.
72                     return false;
73                   }
74                   if (instruction->IsReadOnlyPointer()) {
75                     // Read only: cannot store to it.
76                     return false;
77                   }
78                   switch (instruction->opcode()) {
79                     case SpvOpConstantNull:
80                     case SpvOpUndef:
81                       // Do not allow storing to a null or undefined pointer;
82                       // this might be OK if the block is dead, but for now we
83                       // conservatively avoid it.
84                       return false;
85                     default:
86                       break;
87                   }
88                   return GetTransformationContext()
89                              ->GetFactManager()
90                              ->BlockIsDead(block->id()) ||
91                          GetTransformationContext()
92                              ->GetFactManager()
93                              ->PointeeValueIsIrrelevant(
94                                  instruction->result_id());
95                 });
96 
97         // At this point, |relevant_pointers| contains all the pointers we might
98         // think of storing to.
99         if (relevant_pointers.empty()) {
100           return;
101         }
102 
103         auto pointer = relevant_pointers[GetFuzzerContext()->RandomIndex(
104             relevant_pointers)];
105 
106         std::vector<opt::Instruction*> relevant_values =
107             FindAvailableInstructions(
108                 function, block, inst_it,
109                 [pointer](opt::IRContext* context,
110                           opt::Instruction* instruction) -> bool {
111                   if (!instruction->result_id() || !instruction->type_id()) {
112                     return false;
113                   }
114                   return instruction->type_id() ==
115                          context->get_def_use_mgr()
116                              ->GetDef(pointer->type_id())
117                              ->GetSingleWordInOperand(1);
118                 });
119 
120         if (relevant_values.empty()) {
121           return;
122         }
123 
124         bool is_atomic_store = false;
125         uint32_t memory_scope_id = 0;
126         uint32_t memory_semantics_id = 0;
127 
128         auto storage_class =
129             static_cast<SpvStorageClass>(GetIRContext()
130                                              ->get_def_use_mgr()
131                                              ->GetDef(pointer->type_id())
132                                              ->GetSingleWordInOperand(0));
133 
134         switch (storage_class) {
135           case SpvStorageClassStorageBuffer:
136           case SpvStorageClassPhysicalStorageBuffer:
137           case SpvStorageClassWorkgroup:
138           case SpvStorageClassCrossWorkgroup:
139           case SpvStorageClassAtomicCounter:
140           case SpvStorageClassImage:
141             if (GetFuzzerContext()->ChoosePercentage(
142                     GetFuzzerContext()->GetChanceOfAddingAtomicStore())) {
143               is_atomic_store = true;
144 
145               memory_scope_id = FindOrCreateConstant(
146                   {SpvScopeInvocation},
147                   FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()),
148                   false);
149 
150               memory_semantics_id = FindOrCreateConstant(
151                   {static_cast<uint32_t>(
152                       fuzzerutil::GetMemorySemanticsForStorageClass(
153                           storage_class))},
154                   FindOrCreateIntegerType(32, GetFuzzerContext()->ChooseEven()),
155                   false);
156             }
157             break;
158 
159           default:
160             break;
161         }
162 
163         // Create and apply the transformation.
164         ApplyTransformation(TransformationStore(
165             pointer->result_id(), is_atomic_store, memory_scope_id,
166             memory_semantics_id,
167             relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)]
168                 ->result_id(),
169             instruction_descriptor));
170       });
171 }
172 
173 }  // namespace fuzz
174 }  // namespace spvtools
175