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/replayer.h"
16 
17 #include "gtest/gtest.h"
18 #include "source/fuzz/data_descriptor.h"
19 #include "source/fuzz/fuzzer_util.h"
20 #include "source/fuzz/instruction_descriptor.h"
21 #include "source/fuzz/transformation_add_constant_scalar.h"
22 #include "source/fuzz/transformation_add_global_variable.h"
23 #include "source/fuzz/transformation_add_parameter.h"
24 #include "source/fuzz/transformation_add_synonym.h"
25 #include "source/fuzz/transformation_flatten_conditional_branch.h"
26 #include "source/fuzz/transformation_split_block.h"
27 #include "test/fuzz/fuzz_test_util.h"
28 
29 namespace spvtools {
30 namespace fuzz {
31 namespace {
32 
TEST(ReplayerTest,PartialReplay)33 TEST(ReplayerTest, PartialReplay) {
34   const std::string kTestShader = R"(
35                OpCapability Shader
36           %1 = OpExtInstImport "GLSL.std.450"
37                OpMemoryModel Logical GLSL450
38                OpEntryPoint Fragment %4 "main"
39                OpExecutionMode %4 OriginUpperLeft
40                OpSource ESSL 310
41                OpName %4 "main"
42                OpName %8 "g"
43                OpName %11 "x"
44           %2 = OpTypeVoid
45           %3 = OpTypeFunction %2
46           %6 = OpTypeInt 32 1
47           %7 = OpTypePointer Private %6
48           %8 = OpVariable %7 Private
49           %9 = OpConstant %6 10
50          %10 = OpTypePointer Function %6
51           %4 = OpFunction %2 None %3
52           %5 = OpLabel
53          %11 = OpVariable %10 Function
54                OpStore %8 %9
55          %12 = OpLoad %6 %8
56                OpStore %11 %12
57          %13 = OpLoad %6 %8
58                OpStore %11 %13
59          %14 = OpLoad %6 %8
60                OpStore %11 %14
61          %15 = OpLoad %6 %8
62                OpStore %11 %15
63          %16 = OpLoad %6 %8
64                OpStore %11 %16
65          %17 = OpLoad %6 %8
66                OpStore %11 %17
67          %18 = OpLoad %6 %8
68                OpStore %11 %18
69          %19 = OpLoad %6 %8
70                OpStore %11 %19
71          %20 = OpLoad %6 %8
72                OpStore %11 %20
73          %21 = OpLoad %6 %8
74                OpStore %11 %21
75          %22 = OpLoad %6 %8
76                OpStore %11 %22
77                OpReturn
78                OpFunctionEnd
79   )";
80 
81   const auto env = SPV_ENV_UNIVERSAL_1_3;
82   spvtools::ValidatorOptions validator_options;
83 
84   std::vector<uint32_t> binary_in;
85   SpirvTools t(env);
86   t.SetMessageConsumer(kConsoleMessageConsumer);
87   ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption));
88   ASSERT_TRUE(t.Validate(binary_in));
89 
90   protobufs::TransformationSequence transformations;
91   for (uint32_t id = 12; id <= 22; id++) {
92     *transformations.add_transformation() =
93         TransformationSplitBlock(MakeInstructionDescriptor(id, SpvOpLoad, 0),
94                                  id + 100)
95             .ToMessage();
96   }
97 
98   {
99     // Full replay
100     protobufs::FactSequence empty_facts;
101     auto replayer_result =
102         Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
103                  transformations, 11, true, validator_options)
104             .Run();
105     // Replay should succeed.
106     ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
107               replayer_result.status);
108     // All transformations should be applied.
109     ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
110         transformations, replayer_result.applied_transformations));
111 
112     const std::string kFullySplitShader = R"(
113                OpCapability Shader
114           %1 = OpExtInstImport "GLSL.std.450"
115                OpMemoryModel Logical GLSL450
116                OpEntryPoint Fragment %4 "main"
117                OpExecutionMode %4 OriginUpperLeft
118                OpSource ESSL 310
119                OpName %4 "main"
120                OpName %8 "g"
121                OpName %11 "x"
122           %2 = OpTypeVoid
123           %3 = OpTypeFunction %2
124           %6 = OpTypeInt 32 1
125           %7 = OpTypePointer Private %6
126           %8 = OpVariable %7 Private
127           %9 = OpConstant %6 10
128          %10 = OpTypePointer Function %6
129           %4 = OpFunction %2 None %3
130           %5 = OpLabel
131          %11 = OpVariable %10 Function
132                OpStore %8 %9
133                OpBranch %112
134         %112 = OpLabel
135          %12 = OpLoad %6 %8
136                OpStore %11 %12
137                OpBranch %113
138         %113 = OpLabel
139          %13 = OpLoad %6 %8
140                OpStore %11 %13
141                OpBranch %114
142         %114 = OpLabel
143          %14 = OpLoad %6 %8
144                OpStore %11 %14
145                OpBranch %115
146         %115 = OpLabel
147          %15 = OpLoad %6 %8
148                OpStore %11 %15
149                OpBranch %116
150         %116 = OpLabel
151          %16 = OpLoad %6 %8
152                OpStore %11 %16
153                OpBranch %117
154         %117 = OpLabel
155          %17 = OpLoad %6 %8
156                OpStore %11 %17
157                OpBranch %118
158         %118 = OpLabel
159          %18 = OpLoad %6 %8
160                OpStore %11 %18
161                OpBranch %119
162         %119 = OpLabel
163          %19 = OpLoad %6 %8
164                OpStore %11 %19
165                OpBranch %120
166         %120 = OpLabel
167          %20 = OpLoad %6 %8
168                OpStore %11 %20
169                OpBranch %121
170         %121 = OpLabel
171          %21 = OpLoad %6 %8
172                OpStore %11 %21
173                OpBranch %122
174         %122 = OpLabel
175          %22 = OpLoad %6 %8
176                OpStore %11 %22
177                OpReturn
178                OpFunctionEnd
179     )";
180     ASSERT_TRUE(IsEqual(env, kFullySplitShader,
181                         replayer_result.transformed_module.get()));
182   }
183 
184   {
185     // Half replay
186     protobufs::FactSequence empty_facts;
187     auto replayer_result =
188         Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
189                  transformations, 5, true, validator_options)
190             .Run();
191     // Replay should succeed.
192     ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
193               replayer_result.status);
194     // The first 5 transformations should be applied
195     ASSERT_EQ(5, replayer_result.applied_transformations.transformation_size());
196     for (uint32_t i = 0; i < 5; i++) {
197       ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
198           transformations.transformation(i),
199           replayer_result.applied_transformations.transformation(i)));
200     }
201 
202     const std::string kHalfSplitShader = R"(
203                OpCapability Shader
204           %1 = OpExtInstImport "GLSL.std.450"
205                OpMemoryModel Logical GLSL450
206                OpEntryPoint Fragment %4 "main"
207                OpExecutionMode %4 OriginUpperLeft
208                OpSource ESSL 310
209                OpName %4 "main"
210                OpName %8 "g"
211                OpName %11 "x"
212           %2 = OpTypeVoid
213           %3 = OpTypeFunction %2
214           %6 = OpTypeInt 32 1
215           %7 = OpTypePointer Private %6
216           %8 = OpVariable %7 Private
217           %9 = OpConstant %6 10
218          %10 = OpTypePointer Function %6
219           %4 = OpFunction %2 None %3
220           %5 = OpLabel
221          %11 = OpVariable %10 Function
222                OpStore %8 %9
223                OpBranch %112
224         %112 = OpLabel
225          %12 = OpLoad %6 %8
226                OpStore %11 %12
227                OpBranch %113
228         %113 = OpLabel
229          %13 = OpLoad %6 %8
230                OpStore %11 %13
231                OpBranch %114
232         %114 = OpLabel
233          %14 = OpLoad %6 %8
234                OpStore %11 %14
235                OpBranch %115
236         %115 = OpLabel
237          %15 = OpLoad %6 %8
238                OpStore %11 %15
239                OpBranch %116
240         %116 = OpLabel
241          %16 = OpLoad %6 %8
242                OpStore %11 %16
243          %17 = OpLoad %6 %8
244                OpStore %11 %17
245          %18 = OpLoad %6 %8
246                OpStore %11 %18
247          %19 = OpLoad %6 %8
248                OpStore %11 %19
249          %20 = OpLoad %6 %8
250                OpStore %11 %20
251          %21 = OpLoad %6 %8
252                OpStore %11 %21
253          %22 = OpLoad %6 %8
254                OpStore %11 %22
255                OpReturn
256                OpFunctionEnd
257     )";
258     ASSERT_TRUE(IsEqual(env, kHalfSplitShader,
259                         replayer_result.transformed_module.get()));
260   }
261 
262   {
263     // Empty replay
264     protobufs::FactSequence empty_facts;
265     auto replayer_result =
266         Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
267                  transformations, 0, true, validator_options)
268             .Run();
269     // Replay should succeed.
270     ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete,
271               replayer_result.status);
272     // No transformations should be applied
273     ASSERT_EQ(0, replayer_result.applied_transformations.transformation_size());
274     ASSERT_TRUE(
275         IsEqual(env, kTestShader, replayer_result.transformed_module.get()));
276   }
277 
278   {
279     // Invalid replay: too many transformations
280     protobufs::FactSequence empty_facts;
281     // The number of transformations requested to be applied exceeds the number
282     // of transformations
283     auto replayer_result =
284         Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
285                  transformations, 12, true, validator_options)
286             .Run();
287 
288     // Replay should not succeed.
289     ASSERT_EQ(Replayer::ReplayerResultStatus::kTooManyTransformationsRequested,
290               replayer_result.status);
291     // No transformations should be applied
292     ASSERT_EQ(0, replayer_result.applied_transformations.transformation_size());
293     // The output binary should be empty
294     ASSERT_EQ(nullptr, replayer_result.transformed_module);
295   }
296 }
297 
TEST(ReplayerTest,CheckFactsAfterReplay)298 TEST(ReplayerTest, CheckFactsAfterReplay) {
299   const std::string kTestShader = R"(
300                OpCapability Shader
301           %1 = OpExtInstImport "GLSL.std.450"
302                OpMemoryModel Logical GLSL450
303                OpEntryPoint Fragment %4 "main"
304                OpExecutionMode %4 OriginUpperLeft
305                OpSource ESSL 320
306           %2 = OpTypeVoid
307           %3 = OpTypeFunction %2
308           %8 = OpTypeInt 32 1
309           %9 = OpTypePointer Function %8
310          %50 = OpTypePointer Private %8
311          %11 = OpConstant %8 1
312           %4 = OpFunction %2 None %3
313           %5 = OpLabel
314          %10 = OpVariable %9 Function
315                OpStore %10 %11
316          %12 = OpFunctionCall %2 %6
317                OpReturn
318                OpFunctionEnd
319           %6 = OpFunction %2 None %3
320           %7 = OpLabel
321                OpReturn
322                OpFunctionEnd
323   )";
324 
325   const auto env = SPV_ENV_UNIVERSAL_1_3;
326   spvtools::ValidatorOptions validator_options;
327 
328   std::vector<uint32_t> binary_in;
329   SpirvTools t(env);
330   t.SetMessageConsumer(kConsoleMessageConsumer);
331   ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption));
332   ASSERT_TRUE(t.Validate(binary_in));
333 
334   protobufs::TransformationSequence transformations;
335   *transformations.add_transformation() =
336       TransformationAddConstantScalar(100, 8, {42}, true).ToMessage();
337   *transformations.add_transformation() =
338       TransformationAddGlobalVariable(101, 50, SpvStorageClassPrivate, 100,
339                                       true)
340           .ToMessage();
341   *transformations.add_transformation() =
342       TransformationAddParameter(6, 102, 8, {{12, 100}}, 103).ToMessage();
343   *transformations.add_transformation() =
344       TransformationAddSynonym(
345           11,
346           protobufs::TransformationAddSynonym::SynonymType::
347               TransformationAddSynonym_SynonymType_COPY_OBJECT,
348           104, MakeInstructionDescriptor(12, SpvOpFunctionCall, 0))
349           .ToMessage();
350 
351   // Full replay
352   protobufs::FactSequence empty_facts;
353   auto replayer_result =
354       Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
355                transformations, transformations.transformation_size(), true,
356                validator_options)
357           .Run();
358   // Replay should succeed.
359   ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, replayer_result.status);
360   // All transformations should be applied.
361   ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals(
362       transformations, replayer_result.applied_transformations));
363 
364   const std::string kExpected = R"(
365                OpCapability Shader
366           %1 = OpExtInstImport "GLSL.std.450"
367                OpMemoryModel Logical GLSL450
368                OpEntryPoint Fragment %4 "main"
369                OpExecutionMode %4 OriginUpperLeft
370                OpSource ESSL 320
371           %2 = OpTypeVoid
372           %3 = OpTypeFunction %2
373           %8 = OpTypeInt 32 1
374           %9 = OpTypePointer Function %8
375          %50 = OpTypePointer Private %8
376          %11 = OpConstant %8 1
377         %100 = OpConstant %8 42
378         %101 = OpVariable %50 Private %100
379         %103 = OpTypeFunction %2 %8
380           %4 = OpFunction %2 None %3
381           %5 = OpLabel
382          %10 = OpVariable %9 Function
383                OpStore %10 %11
384         %104 = OpCopyObject %8 %11
385          %12 = OpFunctionCall %2 %6 %100
386                OpReturn
387                OpFunctionEnd
388           %6 = OpFunction %2 None %103
389         %102 = OpFunctionParameter %8
390           %7 = OpLabel
391                OpReturn
392                OpFunctionEnd
393   )";
394   ASSERT_TRUE(
395       IsEqual(env, kExpected, replayer_result.transformed_module.get()));
396 
397   ASSERT_TRUE(
398       replayer_result.transformation_context->GetFactManager()->IdIsIrrelevant(
399           100));
400   ASSERT_TRUE(replayer_result.transformation_context->GetFactManager()
401                   ->PointeeValueIsIrrelevant(101));
402   ASSERT_TRUE(
403       replayer_result.transformation_context->GetFactManager()->IdIsIrrelevant(
404           102));
405   ASSERT_TRUE(
406       replayer_result.transformation_context->GetFactManager()->IsSynonymous(
407           MakeDataDescriptor(11, {}), MakeDataDescriptor(104, {})));
408 }
409 
TEST(ReplayerTest,ReplayWithOverflowIds)410 TEST(ReplayerTest, ReplayWithOverflowIds) {
411   const std::string kTestShader = R"(
412                OpCapability Shader
413           %1 = OpExtInstImport "GLSL.std.450"
414                OpMemoryModel Logical GLSL450
415                OpEntryPoint Fragment %4 "main"
416                OpExecutionMode %4 OriginUpperLeft
417                OpSource ESSL 320
418           %2 = OpTypeVoid
419           %3 = OpTypeFunction %2
420           %6 = OpTypeInt 32 1
421           %7 = OpTypePointer Function %6
422          %50 = OpTypePointer Private %6
423           %9 = OpConstant %6 2
424          %11 = OpConstant %6 0
425          %12 = OpTypeBool
426          %17 = OpConstant %6 1
427           %4 = OpFunction %2 None %3
428           %5 = OpLabel
429           %8 = OpVariable %7 Function
430                OpStore %8 %9
431          %10 = OpLoad %6 %8
432          %13 = OpSGreaterThan %12 %10 %11
433                OpSelectionMerge %15 None
434                OpBranchConditional %13 %14 %15
435          %14 = OpLabel
436          %16 = OpLoad %6 %8
437          %18 = OpIAdd %6 %16 %17
438                OpStore %8 %18
439                OpBranch %15
440          %15 = OpLabel
441                OpReturn
442                OpFunctionEnd
443   )";
444 
445   const auto env = SPV_ENV_UNIVERSAL_1_3;
446   spvtools::ValidatorOptions validator_options;
447 
448   std::vector<uint32_t> binary_in;
449   SpirvTools t(env);
450   t.SetMessageConsumer(kConsoleMessageConsumer);
451   ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption));
452   ASSERT_TRUE(t.Validate(binary_in));
453 
454   protobufs::TransformationSequence transformations;
455   *transformations.add_transformation() =
456       TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}).ToMessage();
457   *transformations.add_transformation() =
458       TransformationAddGlobalVariable(101, 50, SpvStorageClassPrivate, 11, true)
459           .ToMessage();
460 
461   protobufs::FactSequence empty_facts;
462   auto replayer_result =
463       Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts,
464                transformations, transformations.transformation_size(), true,
465                validator_options)
466           .Run();
467   // Replay should succeed.
468   ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, replayer_result.status);
469   // All transformations should be applied.
470   ASSERT_EQ(2, replayer_result.applied_transformations.transformation_size());
471 }
472 
473 }  // namespace
474 }  // namespace fuzz
475 }  // namespace spvtools
476