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_load.h"
16
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19
20 namespace spvtools {
21 namespace fuzz {
22
TransformationLoad(protobufs::TransformationLoad message)23 TransformationLoad::TransformationLoad(protobufs::TransformationLoad message)
24 : message_(std::move(message)) {}
25
TransformationLoad(uint32_t fresh_id,uint32_t pointer_id,bool is_atomic,uint32_t memory_scope,uint32_t memory_semantics,const protobufs::InstructionDescriptor & instruction_to_insert_before)26 TransformationLoad::TransformationLoad(
27 uint32_t fresh_id, uint32_t pointer_id, bool is_atomic,
28 uint32_t memory_scope, uint32_t memory_semantics,
29 const protobufs::InstructionDescriptor& instruction_to_insert_before) {
30 message_.set_fresh_id(fresh_id);
31 message_.set_pointer_id(pointer_id);
32 message_.set_is_atomic(is_atomic);
33 message_.set_memory_scope_id(memory_scope);
34 message_.set_memory_semantics_id(memory_semantics);
35
36 *message_.mutable_instruction_to_insert_before() =
37 instruction_to_insert_before;
38 }
39
IsApplicable(opt::IRContext * ir_context,const TransformationContext &) const40 bool TransformationLoad::IsApplicable(
41 opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
42 // The result id must be fresh.
43 if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
44 return false;
45 }
46
47 // The pointer must exist and have a type.
48 auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
49 if (!pointer || !pointer->type_id()) {
50 return false;
51 }
52 // The type must indeed be a pointer type.
53 auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id());
54 assert(pointer_type && "Type id must be defined.");
55 if (pointer_type->opcode() != SpvOpTypePointer) {
56 return false;
57 }
58 // We do not want to allow loading from null or undefined pointers, as it is
59 // not clear how punishing the consequences of doing so are from a semantics
60 // point of view.
61 switch (pointer->opcode()) {
62 case SpvOpConstantNull:
63 case SpvOpUndef:
64 return false;
65 default:
66 break;
67 }
68
69 // Determine which instruction we should be inserting before.
70 auto insert_before =
71 FindInstruction(message_.instruction_to_insert_before(), ir_context);
72 // It must exist, ...
73 if (!insert_before) {
74 return false;
75 }
76 // ... and it must be legitimate to insert a load before it.
77 if (!message_.is_atomic() &&
78 !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) {
79 return false;
80 }
81
82 if (message_.is_atomic() && !fuzzerutil::CanInsertOpcodeBeforeInstruction(
83 SpvOpAtomicLoad, insert_before)) {
84 return false;
85 }
86
87 if (message_.is_atomic()) {
88 // Check the exists of memory scope and memory semantics ids.
89 auto memory_scope_instruction =
90 ir_context->get_def_use_mgr()->GetDef(message_.memory_scope_id());
91 auto memory_semantics_instruction =
92 ir_context->get_def_use_mgr()->GetDef(message_.memory_semantics_id());
93
94 if (!memory_scope_instruction) {
95 return false;
96 }
97 if (!memory_semantics_instruction) {
98 return false;
99 }
100 // The memory scope and memory semantics instructions must have the
101 // 'OpConstant' opcode.
102 if (memory_scope_instruction->opcode() != SpvOpConstant) {
103 return false;
104 }
105 if (memory_semantics_instruction->opcode() != SpvOpConstant) {
106 return false;
107 }
108 // The memory scope and memory semantics need to be available before
109 // |insert_before|.
110 if (!fuzzerutil::IdIsAvailableBeforeInstruction(
111 ir_context, insert_before, message_.memory_scope_id())) {
112 return false;
113 }
114 if (!fuzzerutil::IdIsAvailableBeforeInstruction(
115 ir_context, insert_before, message_.memory_semantics_id())) {
116 return false;
117 }
118 // The memory scope and memory semantics instructions must have an Integer
119 // operand type with signedness does not matters.
120 if (ir_context->get_def_use_mgr()
121 ->GetDef(memory_scope_instruction->type_id())
122 ->opcode() != SpvOpTypeInt) {
123 return false;
124 }
125 if (ir_context->get_def_use_mgr()
126 ->GetDef(memory_semantics_instruction->type_id())
127 ->opcode() != SpvOpTypeInt) {
128 return false;
129 }
130
131 // The size of the integer for memory scope and memory semantics
132 // instructions must be equal to 32 bits.
133 auto memory_scope_int_width =
134 ir_context->get_def_use_mgr()
135 ->GetDef(memory_scope_instruction->type_id())
136 ->GetSingleWordInOperand(0);
137 auto memory_semantics_int_width =
138 ir_context->get_def_use_mgr()
139 ->GetDef(memory_semantics_instruction->type_id())
140 ->GetSingleWordInOperand(0);
141
142 if (memory_scope_int_width != 32) {
143 return false;
144 }
145 if (memory_semantics_int_width != 32) {
146 return false;
147 }
148
149 // The memory scope constant value must be that of SpvScopeInvocation.
150 auto memory_scope_const_value =
151 memory_scope_instruction->GetSingleWordInOperand(0);
152 if (memory_scope_const_value != SpvScopeInvocation) {
153 return false;
154 }
155
156 // The memory semantics constant value must match the storage class of the
157 // pointer being loaded from.
158 auto memory_semantics_const_value = static_cast<SpvMemorySemanticsMask>(
159 memory_semantics_instruction->GetSingleWordInOperand(0));
160 if (memory_semantics_const_value !=
161 fuzzerutil::GetMemorySemanticsForStorageClass(
162 static_cast<SpvStorageClass>(
163 pointer_type->GetSingleWordInOperand(0)))) {
164 return false;
165 }
166 }
167
168 // The pointer needs to be available at the insertion point.
169 return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before,
170 message_.pointer_id());
171 }
172
Apply(opt::IRContext * ir_context,TransformationContext *) const173 void TransformationLoad::Apply(opt::IRContext* ir_context,
174 TransformationContext* /*unused*/) const {
175 if (message_.is_atomic()) {
176 // OpAtomicLoad instruction.
177 uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
178 ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
179 fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
180 auto insert_before =
181 FindInstruction(message_.instruction_to_insert_before(), ir_context);
182 auto new_instruction = MakeUnique<opt::Instruction>(
183 ir_context, SpvOpAtomicLoad, result_type, message_.fresh_id(),
184 opt::Instruction::OperandList(
185 {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
186 {SPV_OPERAND_TYPE_SCOPE_ID, {message_.memory_scope_id()}},
187 {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID,
188 {message_.memory_semantics_id()}}}));
189 auto new_instruction_ptr = new_instruction.get();
190 insert_before->InsertBefore(std::move(new_instruction));
191 // Inform the def-use manager about the new instruction and record its basic
192 // block.
193 ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
194 ir_context->set_instr_block(new_instruction_ptr,
195 ir_context->get_instr_block(insert_before));
196 } else {
197 // OpLoad instruction.
198 uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
199 ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
200 fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
201 auto insert_before =
202 FindInstruction(message_.instruction_to_insert_before(), ir_context);
203 auto new_instruction = MakeUnique<opt::Instruction>(
204 ir_context, SpvOpLoad, result_type, message_.fresh_id(),
205 opt::Instruction::OperandList(
206 {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}));
207 auto new_instruction_ptr = new_instruction.get();
208 insert_before->InsertBefore(std::move(new_instruction));
209 // Inform the def-use manager about the new instruction and record its basic
210 // block.
211 ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
212 ir_context->set_instr_block(new_instruction_ptr,
213 ir_context->get_instr_block(insert_before));
214 }
215 }
216
ToMessage() const217 protobufs::Transformation TransformationLoad::ToMessage() const {
218 protobufs::Transformation result;
219 *result.mutable_load() = message_;
220 return result;
221 }
222
GetFreshIds() const223 std::unordered_set<uint32_t> TransformationLoad::GetFreshIds() const {
224 return {message_.fresh_id()};
225 }
226
227 } // namespace fuzz
228 } // namespace spvtools
229