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 "gtest/gtest.h"
18 #include "source/fuzz/fuzzer_util.h"
19 #include "source/fuzz/instruction_descriptor.h"
20 #include "test/fuzz/fuzz_test_util.h"
21
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25
TEST(TransformationMutatePointerTest, BasicTest)26 TEST(TransformationMutatePointerTest, BasicTest) {
27 std::string shader = R"(
28 OpCapability Shader
29 OpCapability VariablePointers
30 %1 = OpExtInstImport "GLSL.std.450"
31 OpMemoryModel Logical GLSL450
32 OpEntryPoint Fragment %4 "main"
33 OpExecutionMode %4 OriginUpperLeft
34 OpSource ESSL 310
35 %2 = OpTypeVoid
36 %3 = OpTypeFunction %2
37 %6 = OpTypeInt 32 1
38 %7 = OpTypeFloat 32
39 %34 = OpConstant %7 0
40 %36 = OpConstant %6 0
41 %14 = OpTypeVector %7 3
42 %35 = OpConstantComposite %14 %34 %34 %34
43 %15 = OpTypeMatrix %14 2
44 %8 = OpConstant %6 5
45 %9 = OpTypeArray %7 %8
46 %37 = OpConstantComposite %9 %34 %34 %34 %34 %34
47 %11 = OpTypeStruct
48 %38 = OpConstantComposite %11
49 %39 = OpConstantComposite %15 %35 %35
50 %31 = OpTypePointer Function %14
51 %10 = OpTypeStruct %7 %6 %9 %11 %15 %14
52 %40 = OpConstantComposite %10 %34 %36 %37 %38 %39 %35
53 %13 = OpTypePointer Function %10
54 %16 = OpTypePointer Private %10
55 %17 = OpTypePointer Workgroup %10
56 %18 = OpTypeStruct %16
57 %19 = OpTypePointer Private %18
58 %20 = OpVariable %16 Private
59 %21 = OpVariable %17 Workgroup
60 %22 = OpVariable %19 Private
61 %23 = OpTypePointer Output %6
62 %24 = OpVariable %23 Output
63 %27 = OpTypeFunction %2 %13
64 %33 = OpConstantNull %16
65 %4 = OpFunction %2 None %3
66 %5 = OpLabel
67 OpReturn
68 OpFunctionEnd
69 %28 = OpFunction %2 None %27
70 %29 = OpFunctionParameter %13
71 %30 = OpLabel
72 %25 = OpVariable %13 Function
73 %26 = OpAccessChain %31 %25 %8
74 OpReturn
75 OpFunctionEnd
76 )";
77
78 const auto env = SPV_ENV_UNIVERSAL_1_3;
79 const auto consumer = nullptr;
80 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
81 spvtools::ValidatorOptions validator_options;
82 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
83 kConsoleMessageConsumer));
84 TransformationContext transformation_context(
85 MakeUnique<FactManager>(context.get()), validator_options);
86 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(35);
87 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(39);
88
89 const auto insert_before =
90 MakeInstructionDescriptor(26, spv::Op::OpReturn, 0);
91
92 // 20 is not a fresh id.
93 ASSERT_FALSE(TransformationMutatePointer(20, 20, insert_before)
94 .IsApplicable(context.get(), transformation_context));
95
96 // |insert_before| instruction descriptor is invalid.
97 ASSERT_FALSE(TransformationMutatePointer(
98 20, 70, MakeInstructionDescriptor(26, spv::Op::OpStore, 0))
99 .IsApplicable(context.get(), transformation_context));
100
101 // Can't insert OpLoad before OpVariable.
102 ASSERT_FALSE(
103 TransformationMutatePointer(
104 20, 70, MakeInstructionDescriptor(26, spv::Op::OpVariable, 0))
105 .IsApplicable(context.get(), transformation_context));
106
107 // |pointer_id| doesn't exist in the module.
108 ASSERT_FALSE(TransformationMutatePointer(70, 70, insert_before)
109 .IsApplicable(context.get(), transformation_context));
110
111 // |pointer_id| doesn't have a type id.
112 ASSERT_FALSE(TransformationMutatePointer(11, 70, insert_before)
113 .IsApplicable(context.get(), transformation_context));
114
115 // |pointer_id| is a result id of OpConstantNull.
116 ASSERT_FALSE(TransformationMutatePointer(33, 70, insert_before)
117 .IsApplicable(context.get(), transformation_context));
118
119 // |pointer_id| is not a pointer instruction.
120 ASSERT_FALSE(TransformationMutatePointer(8, 70, insert_before)
121 .IsApplicable(context.get(), transformation_context));
122
123 // |pointer_id| has invalid storage class
124 ASSERT_FALSE(TransformationMutatePointer(24, 70, insert_before)
125 .IsApplicable(context.get(), transformation_context));
126
127 // |pointer_id|'s pointee contains non-scalar and non-composite constituents.
128 ASSERT_FALSE(TransformationMutatePointer(22, 70, insert_before)
129 .IsApplicable(context.get(), transformation_context));
130
131 // There is no irrelevant zero constant to insert into the |pointer_id|.
132 ASSERT_FALSE(TransformationMutatePointer(20, 70, insert_before)
133 .IsApplicable(context.get(), transformation_context));
134
135 // |pointer_id| is not available before |insert_before|.
136 ASSERT_FALSE(
137 TransformationMutatePointer(
138 26, 70, MakeInstructionDescriptor(26, spv::Op::OpAccessChain, 0))
139 .IsApplicable(context.get(), transformation_context));
140
141 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(40);
142
143 uint32_t fresh_id = 70;
144 uint32_t pointer_ids[] = {
145 20, // Mutate Private variable.
146 21, // Mutate Workgroup variable.
147 25, // Mutate Function variable.
148 29, // Mutate function parameter.
149 26, // Mutate OpAccessChain.
150 };
151
152 for (auto pointer_id : pointer_ids) {
153 TransformationMutatePointer transformation(pointer_id, fresh_id++,
154 insert_before);
155 ASSERT_TRUE(
156 transformation.IsApplicable(context.get(), transformation_context));
157 ApplyAndCheckFreshIds(transformation, context.get(),
158 &transformation_context);
159 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
160 context.get(), validator_options, kConsoleMessageConsumer));
161 }
162
163 std::string after_transformation = R"(
164 OpCapability Shader
165 OpCapability VariablePointers
166 %1 = OpExtInstImport "GLSL.std.450"
167 OpMemoryModel Logical GLSL450
168 OpEntryPoint Fragment %4 "main"
169 OpExecutionMode %4 OriginUpperLeft
170 OpSource ESSL 310
171 %2 = OpTypeVoid
172 %3 = OpTypeFunction %2
173 %6 = OpTypeInt 32 1
174 %7 = OpTypeFloat 32
175 %34 = OpConstant %7 0
176 %36 = OpConstant %6 0
177 %14 = OpTypeVector %7 3
178 %35 = OpConstantComposite %14 %34 %34 %34
179 %15 = OpTypeMatrix %14 2
180 %8 = OpConstant %6 5
181 %9 = OpTypeArray %7 %8
182 %37 = OpConstantComposite %9 %34 %34 %34 %34 %34
183 %11 = OpTypeStruct
184 %38 = OpConstantComposite %11
185 %39 = OpConstantComposite %15 %35 %35
186 %31 = OpTypePointer Function %14
187 %10 = OpTypeStruct %7 %6 %9 %11 %15 %14
188 %40 = OpConstantComposite %10 %34 %36 %37 %38 %39 %35
189 %13 = OpTypePointer Function %10
190 %16 = OpTypePointer Private %10
191 %17 = OpTypePointer Workgroup %10
192 %18 = OpTypeStruct %16
193 %19 = OpTypePointer Private %18
194 %20 = OpVariable %16 Private
195 %21 = OpVariable %17 Workgroup
196 %22 = OpVariable %19 Private
197 %23 = OpTypePointer Output %6
198 %24 = OpVariable %23 Output
199 %27 = OpTypeFunction %2 %13
200 %33 = OpConstantNull %16
201 %4 = OpFunction %2 None %3
202 %5 = OpLabel
203 OpReturn
204 OpFunctionEnd
205 %28 = OpFunction %2 None %27
206 %29 = OpFunctionParameter %13
207 %30 = OpLabel
208 %25 = OpVariable %13 Function
209 %26 = OpAccessChain %31 %25 %8
210
211 ; modified Private variable
212 %70 = OpLoad %10 %20
213 OpStore %20 %40
214 OpStore %20 %70
215
216 ; modified Workgroup variable
217 %71 = OpLoad %10 %21
218 OpStore %21 %40
219 OpStore %21 %71
220
221 ; modified Function variable
222 %72 = OpLoad %10 %25
223 OpStore %25 %40
224 OpStore %25 %72
225
226 ; modified function parameter
227 %73 = OpLoad %10 %29
228 OpStore %29 %40
229 OpStore %29 %73
230
231 ; modified OpAccessChain
232 %74 = OpLoad %14 %26
233 OpStore %26 %35
234 OpStore %26 %74
235
236 OpReturn
237 OpFunctionEnd
238 )";
239
240 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
241 }
242
TEST(TransformationMutatePointerTest, HandlesUnreachableBlocks)243 TEST(TransformationMutatePointerTest, HandlesUnreachableBlocks) {
244 std::string shader = R"(
245 OpCapability Shader
246 %1 = OpExtInstImport "GLSL.std.450"
247 OpMemoryModel Logical GLSL450
248 OpEntryPoint Fragment %4 "main"
249 OpExecutionMode %4 OriginUpperLeft
250 OpSource ESSL 310
251 %2 = OpTypeVoid
252 %3 = OpTypeFunction %2
253 %6 = OpTypeInt 32 1
254 %7 = OpConstant %6 0
255 %8 = OpTypePointer Function %6
256 %11 = OpTypePointer Private %6
257 %12 = OpVariable %11 Private
258 %4 = OpFunction %2 None %3
259 %5 = OpLabel
260 %9 = OpVariable %8 Function
261 OpReturn
262 %10 = OpLabel
263 OpReturn
264 OpFunctionEnd
265 )";
266
267 const auto env = SPV_ENV_UNIVERSAL_1_3;
268 const auto consumer = nullptr;
269 const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
270 spvtools::ValidatorOptions validator_options;
271 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
272 kConsoleMessageConsumer));
273 TransformationContext transformation_context(
274 MakeUnique<FactManager>(context.get()), validator_options);
275 transformation_context.GetFactManager()->AddFactIdIsIrrelevant(7);
276
277 ASSERT_FALSE(
278 context->GetDominatorAnalysis(context->GetFunction(4))->IsReachable(10));
279
280 const auto insert_before =
281 MakeInstructionDescriptor(10, spv::Op::OpReturn, 0);
282
283 // Can mutate a global variable in an unreachable block.
284 TransformationMutatePointer transformation(12, 50, insert_before);
285 ASSERT_TRUE(
286 transformation.IsApplicable(context.get(), transformation_context));
287 ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
288 ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
289 kConsoleMessageConsumer));
290
291 std::string after_transformation = R"(
292 OpCapability Shader
293 %1 = OpExtInstImport "GLSL.std.450"
294 OpMemoryModel Logical GLSL450
295 OpEntryPoint Fragment %4 "main"
296 OpExecutionMode %4 OriginUpperLeft
297 OpSource ESSL 310
298 %2 = OpTypeVoid
299 %3 = OpTypeFunction %2
300 %6 = OpTypeInt 32 1
301 %7 = OpConstant %6 0
302 %8 = OpTypePointer Function %6
303 %11 = OpTypePointer Private %6
304 %12 = OpVariable %11 Private
305 %4 = OpFunction %2 None %3
306 %5 = OpLabel
307 %9 = OpVariable %8 Function
308 OpReturn
309 %10 = OpLabel
310 %50 = OpLoad %6 %12
311 OpStore %12 %7
312 OpStore %12 %50
313 OpReturn
314 OpFunctionEnd
315 )";
316
317 ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
318 }
319
320 } // namespace
321 } // namespace fuzz
322 } // namespace spvtools
323