1 // Copyright (c) 2020 Vasyl Teliman
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_mutate_pointer.h"
16
17 #include "source/fuzz/fuzzer_util.h"
18 #include "source/fuzz/instruction_descriptor.h"
19
20 namespace spvtools {
21 namespace fuzz {
22
TransformationMutatePointer(protobufs::TransformationMutatePointer message)23 TransformationMutatePointer::TransformationMutatePointer(
24 protobufs::TransformationMutatePointer message)
25 : message_(std::move(message)) {}
26
TransformationMutatePointer(uint32_t pointer_id,uint32_t fresh_id,const protobufs::InstructionDescriptor & insert_before)27 TransformationMutatePointer::TransformationMutatePointer(
28 uint32_t pointer_id, uint32_t fresh_id,
29 const protobufs::InstructionDescriptor& insert_before) {
30 message_.set_pointer_id(pointer_id);
31 message_.set_fresh_id(fresh_id);
32 *message_.mutable_insert_before() = insert_before;
33 }
34
IsApplicable(opt::IRContext * ir_context,const TransformationContext & transformation_context) const35 bool TransformationMutatePointer::IsApplicable(
36 opt::IRContext* ir_context,
37 const TransformationContext& transformation_context) const {
38 // Check that |fresh_id| is fresh.
39 if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
40 return false;
41 }
42
43 auto* insert_before_inst =
44 FindInstruction(message_.insert_before(), ir_context);
45
46 // Check that |insert_before| is a valid instruction descriptor.
47 if (!insert_before_inst) {
48 return false;
49 }
50
51 // Check that it is possible to insert OpLoad and OpStore before
52 // |insert_before_inst|. We are only using OpLoad here since the result does
53 // not depend on the opcode.
54 if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpLoad,
55 insert_before_inst)) {
56 return false;
57 }
58
59 const auto* pointer_inst =
60 ir_context->get_def_use_mgr()->GetDef(message_.pointer_id());
61
62 // Check that |pointer_id| is a result id of a valid pointer instruction.
63 if (!pointer_inst || !IsValidPointerInstruction(ir_context, *pointer_inst)) {
64 return false;
65 }
66
67 // Check that the module contains an irrelevant constant that will be used to
68 // mutate |pointer_inst|. The constant is irrelevant so that the latter
69 // transformation can change its value to something more interesting.
70 auto constant_id = fuzzerutil::MaybeGetZeroConstant(
71 ir_context, transformation_context,
72 fuzzerutil::GetPointeeTypeIdFromPointerType(ir_context,
73 pointer_inst->type_id()),
74 true);
75 if (!constant_id) {
76 return false;
77 }
78
79 assert(fuzzerutil::IdIsAvailableBeforeInstruction(
80 ir_context, insert_before_inst, constant_id) &&
81 "Global constant instruction is not available before "
82 "|insert_before_inst|");
83
84 // Check that |pointer_inst| is available before |insert_before_inst|.
85 return fuzzerutil::IdIsAvailableBeforeInstruction(
86 ir_context, insert_before_inst, pointer_inst->result_id());
87 }
88
Apply(opt::IRContext * ir_context,TransformationContext * transformation_context) const89 void TransformationMutatePointer::Apply(
90 opt::IRContext* ir_context,
91 TransformationContext* transformation_context) const {
92 auto* insert_before_inst =
93 FindInstruction(message_.insert_before(), ir_context);
94 assert(insert_before_inst && "|insert_before| descriptor is invalid");
95 opt::BasicBlock* enclosing_block =
96 ir_context->get_instr_block(insert_before_inst);
97
98 auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
99 ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
100
101 // Back up the original value.
102 auto backup_instruction = MakeUnique<opt::Instruction>(
103 ir_context, spv::Op::OpLoad, pointee_type_id, message_.fresh_id(),
104 opt::Instruction::OperandList{
105 {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}});
106 auto backup_instruction_ptr = backup_instruction.get();
107 insert_before_inst->InsertBefore(std::move(backup_instruction));
108 ir_context->get_def_use_mgr()->AnalyzeInstDefUse(backup_instruction_ptr);
109 ir_context->set_instr_block(backup_instruction_ptr, enclosing_block);
110
111 // Insert a new value.
112 auto new_value_instruction = MakeUnique<opt::Instruction>(
113 ir_context, spv::Op::OpStore, 0, 0,
114 opt::Instruction::OperandList{
115 {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
116 {SPV_OPERAND_TYPE_ID,
117 {fuzzerutil::MaybeGetZeroConstant(
118 ir_context, *transformation_context, pointee_type_id, true)}}});
119 auto new_value_instruction_ptr = new_value_instruction.get();
120 insert_before_inst->InsertBefore(std::move(new_value_instruction));
121 ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_value_instruction_ptr);
122 ir_context->set_instr_block(new_value_instruction_ptr, enclosing_block);
123
124 // Restore the original value.
125 auto restore_instruction = MakeUnique<opt::Instruction>(
126 ir_context, spv::Op::OpStore, 0, 0,
127 opt::Instruction::OperandList{
128 {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
129 {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}});
130 auto restore_instruction_ptr = restore_instruction.get();
131 insert_before_inst->InsertBefore(std::move(restore_instruction));
132 ir_context->get_def_use_mgr()->AnalyzeInstDefUse(restore_instruction_ptr);
133 ir_context->set_instr_block(restore_instruction_ptr, enclosing_block);
134
135 fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
136 }
137
ToMessage() const138 protobufs::Transformation TransformationMutatePointer::ToMessage() const {
139 protobufs::Transformation result;
140 *result.mutable_mutate_pointer() = message_;
141 return result;
142 }
143
IsValidPointerInstruction(opt::IRContext * ir_context,const opt::Instruction & inst)144 bool TransformationMutatePointer::IsValidPointerInstruction(
145 opt::IRContext* ir_context, const opt::Instruction& inst) {
146 // |inst| must have both result id and type id and it may not cause undefined
147 // behaviour.
148 if (!inst.result_id() || !inst.type_id() ||
149 inst.opcode() == spv::Op::OpUndef ||
150 inst.opcode() == spv::Op::OpConstantNull) {
151 return false;
152 }
153
154 opt::Instruction* type_inst =
155 ir_context->get_def_use_mgr()->GetDef(inst.type_id());
156 assert(type_inst != nullptr && "|inst| has invalid type id");
157
158 // |inst| must be a pointer.
159 if (type_inst->opcode() != spv::Op::OpTypePointer) {
160 return false;
161 }
162
163 // |inst| must have a supported storage class.
164 switch (
165 static_cast<spv::StorageClass>(type_inst->GetSingleWordInOperand(0))) {
166 case spv::StorageClass::Function:
167 case spv::StorageClass::Private:
168 case spv::StorageClass::Workgroup:
169 break;
170 default:
171 return false;
172 }
173
174 // |inst|'s pointee must consist of scalars and/or composites.
175 return fuzzerutil::CanCreateConstant(ir_context,
176 type_inst->GetSingleWordInOperand(1));
177 }
178
GetFreshIds() const179 std::unordered_set<uint32_t> TransformationMutatePointer::GetFreshIds() const {
180 return {message_.fresh_id()};
181 }
182
183 } // namespace fuzz
184 } // namespace spvtools
185