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