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