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_function_call.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
22namespace spvtools {
23namespace fuzz {
24namespace {
25
26TEST(TransformationFunctionCallTest, BasicTest) {
27  std::string shader = R"(
28               OpCapability Shader
29          %1 = OpExtInstImport "GLSL.std.450"
30               OpMemoryModel Logical GLSL450
31               OpEntryPoint Fragment %4 "main"
32               OpExecutionMode %4 OriginUpperLeft
33               OpSource ESSL 310
34          %2 = OpTypeVoid
35          %3 = OpTypeFunction %2
36          %6 = OpTypeInt 32 1
37          %7 = OpTypePointer Function %6
38          %8 = OpTypeFunction %6 %7
39         %12 = OpTypeFloat 32
40         %13 = OpTypePointer Function %12
41         %14 = OpTypeFunction %6 %7 %13
42         %27 = OpConstant %6 1
43         %50 = OpConstant %12 1
44         %57 = OpTypeBool
45         %58 = OpConstantFalse %57
46        %204 = OpUndef %6
47          %4 = OpFunction %2 None %3
48          %5 = OpLabel
49         %61 = OpVariable %7 Function
50         %62 = OpVariable %7 Function
51         %65 = OpVariable %13 Function
52         %66 = OpVariable %7 Function
53         %68 = OpVariable %13 Function
54         %71 = OpVariable %7 Function
55         %72 = OpVariable %13 Function
56         %73 = OpVariable %7 Function
57         %75 = OpVariable %13 Function
58         %78 = OpVariable %7 Function
59         %98 = OpAccessChain %7 %71
60         %99 = OpCopyObject %7 %71
61               OpSelectionMerge %60 None
62               OpBranchConditional %58 %59 %60
63         %59 = OpLabel
64               OpBranch %60
65         %60 = OpLabel
66               OpReturn
67               OpFunctionEnd
68         %10 = OpFunction %6 None %8
69          %9 = OpFunctionParameter %7
70         %11 = OpLabel
71         %26 = OpLoad %6 %9
72         %28 = OpIAdd %6 %26 %27
73               OpSelectionMerge %97 None
74               OpBranchConditional %58 %96 %97
75         %96 = OpLabel
76               OpBranch %97
77         %97 = OpLabel
78               OpReturnValue %28
79               OpFunctionEnd
80         %17 = OpFunction %6 None %14
81         %15 = OpFunctionParameter %7
82         %16 = OpFunctionParameter %13
83         %18 = OpLabel
84         %31 = OpVariable %7 Function
85         %32 = OpLoad %6 %15
86               OpStore %31 %32
87         %33 = OpFunctionCall %6 %10 %31
88               OpReturnValue %33
89               OpFunctionEnd
90         %21 = OpFunction %6 None %14
91         %19 = OpFunctionParameter %7
92         %20 = OpFunctionParameter %13
93         %22 = OpLabel
94         %36 = OpLoad %6 %19
95         %37 = OpLoad %12 %20
96         %38 = OpConvertFToS %6 %37
97         %39 = OpIAdd %6 %36 %38
98               OpReturnValue %39
99               OpFunctionEnd
100         %24 = OpFunction %6 None %8
101         %23 = OpFunctionParameter %7
102         %25 = OpLabel
103         %44 = OpVariable %7 Function
104         %46 = OpVariable %13 Function
105         %51 = OpVariable %7 Function
106         %52 = OpVariable %13 Function
107         %42 = OpLoad %6 %23
108         %43 = OpConvertSToF %12 %42
109         %45 = OpLoad %6 %23
110               OpStore %44 %45
111               OpStore %46 %43
112         %47 = OpFunctionCall %6 %17 %44 %46
113         %48 = OpLoad %6 %23
114         %49 = OpIAdd %6 %48 %27
115               OpStore %51 %49
116               OpStore %52 %50
117         %53 = OpFunctionCall %6 %17 %51 %52
118         %54 = OpIAdd %6 %47 %53
119               OpReturnValue %54
120               OpFunctionEnd
121        %200 = OpFunction %6 None %14
122        %201 = OpFunctionParameter %7
123        %202 = OpFunctionParameter %13
124        %203 = OpLabel
125               OpSelectionMerge %206 None
126               OpBranchConditional %58 %205 %206
127        %205 = OpLabel
128               OpBranch %206
129        %206 = OpLabel
130               OpReturnValue %204
131               OpFunctionEnd
132  )";
133
134  const auto env = SPV_ENV_UNIVERSAL_1_4;
135  const auto consumer = nullptr;
136  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
137  spvtools::ValidatorOptions validator_options;
138  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
139                                               kConsoleMessageConsumer));
140  TransformationContext transformation_context(
141      MakeUnique<FactManager>(context.get()), validator_options);
142  transformation_context.GetFactManager()->AddFactBlockIsDead(59);
143  transformation_context.GetFactManager()->AddFactBlockIsDead(11);
144  transformation_context.GetFactManager()->AddFactBlockIsDead(18);
145  transformation_context.GetFactManager()->AddFactBlockIsDead(25);
146  transformation_context.GetFactManager()->AddFactBlockIsDead(96);
147  transformation_context.GetFactManager()->AddFactBlockIsDead(205);
148  transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(21);
149  transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(200);
150  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
151      71);
152  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
153      72);
154  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
155      19);
156  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
157      20);
158  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
159      23);
160  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
161      44);
162  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
163      46);
164  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
165      51);
166  transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
167      52);
168
169  // Livesafe functions with argument types: 21(7, 13), 200(7, 13)
170  // Non-livesafe functions with argument types: 4(), 10(7), 17(7, 13), 24(7)
171  // Call graph edges:
172  //    17 -> 10
173  //    24 -> 17
174
175  // Bad transformations
176  // Too many arguments
177  ASSERT_FALSE(TransformationFunctionCall(
178                   100, 21, {71, 72, 71},
179                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
180                   .IsApplicable(context.get(), transformation_context));
181  // Too few arguments
182  ASSERT_FALSE(
183      TransformationFunctionCall(
184          100, 21, {71}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
185          .IsApplicable(context.get(), transformation_context));
186  // Arguments are the wrong way around (types do not match)
187  ASSERT_FALSE(TransformationFunctionCall(
188                   100, 21, {72, 71},
189                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
190                   .IsApplicable(context.get(), transformation_context));
191  // 21 is not an appropriate argument
192  ASSERT_FALSE(TransformationFunctionCall(
193                   100, 21, {21, 72},
194                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
195                   .IsApplicable(context.get(), transformation_context));
196  // 300 does not exist
197  ASSERT_FALSE(TransformationFunctionCall(
198                   100, 21, {300, 72},
199                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
200                   .IsApplicable(context.get(), transformation_context));
201  // 71 is not a function
202  ASSERT_FALSE(TransformationFunctionCall(
203                   100, 71, {71, 72},
204                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
205                   .IsApplicable(context.get(), transformation_context));
206  // 500 does not exist
207  ASSERT_FALSE(TransformationFunctionCall(
208                   100, 500, {71, 72},
209                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
210                   .IsApplicable(context.get(), transformation_context));
211  // Id is not fresh
212  ASSERT_FALSE(
213      TransformationFunctionCall(
214          21, 21, {71, 72}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
215          .IsApplicable(context.get(), transformation_context));
216  // Access chain as pointer parameter
217  ASSERT_FALSE(TransformationFunctionCall(
218                   100, 21, {98, 72},
219                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
220                   .IsApplicable(context.get(), transformation_context));
221  // Copied object as pointer parameter
222  ASSERT_FALSE(TransformationFunctionCall(
223                   100, 21, {99, 72},
224                   MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
225                   .IsApplicable(context.get(), transformation_context));
226  // Non-livesafe called from original live block
227  ASSERT_FALSE(TransformationFunctionCall(
228                   100, 10, {71},
229                   MakeInstructionDescriptor(99, spv::Op::OpSelectionMerge, 0))
230                   .IsApplicable(context.get(), transformation_context));
231  // Non-livesafe called from livesafe function
232  ASSERT_FALSE(TransformationFunctionCall(
233                   100, 10, {19},
234                   MakeInstructionDescriptor(38, spv::Op::OpConvertFToS, 0))
235                   .IsApplicable(context.get(), transformation_context));
236  // Livesafe function called with pointer to non-arbitrary local variable
237  ASSERT_FALSE(TransformationFunctionCall(
238                   100, 21, {61, 72},
239                   MakeInstructionDescriptor(38, spv::Op::OpConvertFToS, 0))
240                   .IsApplicable(context.get(), transformation_context));
241  // Direct recursion
242  ASSERT_FALSE(
243      TransformationFunctionCall(
244          100, 4, {}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0))
245          .IsApplicable(context.get(), transformation_context));
246  // Indirect recursion
247  ASSERT_FALSE(
248      TransformationFunctionCall(
249          100, 24, {9}, MakeInstructionDescriptor(96, spv::Op::OpBranch, 0))
250          .IsApplicable(context.get(), transformation_context));
251  // Parameter 23 is not available at the call site
252  ASSERT_FALSE(
253      TransformationFunctionCall(
254          104, 10, {23}, MakeInstructionDescriptor(205, spv::Op::OpBranch, 0))
255          .IsApplicable(context.get(), transformation_context));
256
257  // Good transformations
258  {
259    // Livesafe called from dead block: fine
260    TransformationFunctionCall transformation(
261        100, 21, {71, 72}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0));
262    ASSERT_TRUE(
263        transformation.IsApplicable(context.get(), transformation_context));
264    ApplyAndCheckFreshIds(transformation, context.get(),
265                          &transformation_context);
266    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
267        context.get(), validator_options, kConsoleMessageConsumer));
268  }
269  {
270    // Livesafe called from original live block: fine
271    TransformationFunctionCall transformation(
272        101, 21, {71, 72},
273        MakeInstructionDescriptor(98, spv::Op::OpAccessChain, 0));
274    ASSERT_TRUE(
275        transformation.IsApplicable(context.get(), transformation_context));
276    ApplyAndCheckFreshIds(transformation, context.get(),
277                          &transformation_context);
278    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
279        context.get(), validator_options, kConsoleMessageConsumer));
280  }
281  {
282    // Livesafe called from livesafe function: fine
283    TransformationFunctionCall transformation(
284        102, 200, {19, 20}, MakeInstructionDescriptor(36, spv::Op::OpLoad, 0));
285    ASSERT_TRUE(
286        transformation.IsApplicable(context.get(), transformation_context));
287    ApplyAndCheckFreshIds(transformation, context.get(),
288                          &transformation_context);
289    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
290        context.get(), validator_options, kConsoleMessageConsumer));
291  }
292  {
293    // Dead called from dead block in injected function: fine
294    TransformationFunctionCall transformation(
295        103, 10, {23}, MakeInstructionDescriptor(45, spv::Op::OpLoad, 0));
296    ASSERT_TRUE(
297        transformation.IsApplicable(context.get(), transformation_context));
298    ApplyAndCheckFreshIds(transformation, context.get(),
299                          &transformation_context);
300    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
301        context.get(), validator_options, kConsoleMessageConsumer));
302  }
303  {
304    // Non-livesafe called from dead block in livesafe function: OK
305    TransformationFunctionCall transformation(
306        104, 10, {201}, MakeInstructionDescriptor(205, spv::Op::OpBranch, 0));
307    ASSERT_TRUE(
308        transformation.IsApplicable(context.get(), transformation_context));
309    ApplyAndCheckFreshIds(transformation, context.get(),
310                          &transformation_context);
311    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
312        context.get(), validator_options, kConsoleMessageConsumer));
313  }
314  {
315    // Livesafe called from dead block with non-arbitrary parameter
316    TransformationFunctionCall transformation(
317        105, 21, {62, 65}, MakeInstructionDescriptor(59, spv::Op::OpBranch, 0));
318    ASSERT_TRUE(
319        transformation.IsApplicable(context.get(), transformation_context));
320    ApplyAndCheckFreshIds(transformation, context.get(),
321                          &transformation_context);
322    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(
323        context.get(), validator_options, kConsoleMessageConsumer));
324  }
325
326  std::string after_transformation = R"(
327               OpCapability Shader
328          %1 = OpExtInstImport "GLSL.std.450"
329               OpMemoryModel Logical GLSL450
330               OpEntryPoint Fragment %4 "main"
331               OpExecutionMode %4 OriginUpperLeft
332               OpSource ESSL 310
333          %2 = OpTypeVoid
334          %3 = OpTypeFunction %2
335          %6 = OpTypeInt 32 1
336          %7 = OpTypePointer Function %6
337          %8 = OpTypeFunction %6 %7
338         %12 = OpTypeFloat 32
339         %13 = OpTypePointer Function %12
340         %14 = OpTypeFunction %6 %7 %13
341         %27 = OpConstant %6 1
342         %50 = OpConstant %12 1
343         %57 = OpTypeBool
344         %58 = OpConstantFalse %57
345        %204 = OpUndef %6
346          %4 = OpFunction %2 None %3
347          %5 = OpLabel
348         %61 = OpVariable %7 Function
349         %62 = OpVariable %7 Function
350         %65 = OpVariable %13 Function
351         %66 = OpVariable %7 Function
352         %68 = OpVariable %13 Function
353         %71 = OpVariable %7 Function
354         %72 = OpVariable %13 Function
355         %73 = OpVariable %7 Function
356         %75 = OpVariable %13 Function
357         %78 = OpVariable %7 Function
358        %101 = OpFunctionCall %6 %21 %71 %72
359         %98 = OpAccessChain %7 %71
360         %99 = OpCopyObject %7 %71
361               OpSelectionMerge %60 None
362               OpBranchConditional %58 %59 %60
363         %59 = OpLabel
364        %100 = OpFunctionCall %6 %21 %71 %72
365        %105 = OpFunctionCall %6 %21 %62 %65
366               OpBranch %60
367         %60 = OpLabel
368               OpReturn
369               OpFunctionEnd
370         %10 = OpFunction %6 None %8
371          %9 = OpFunctionParameter %7
372         %11 = OpLabel
373         %26 = OpLoad %6 %9
374         %28 = OpIAdd %6 %26 %27
375               OpSelectionMerge %97 None
376               OpBranchConditional %58 %96 %97
377         %96 = OpLabel
378               OpBranch %97
379         %97 = OpLabel
380               OpReturnValue %28
381               OpFunctionEnd
382         %17 = OpFunction %6 None %14
383         %15 = OpFunctionParameter %7
384         %16 = OpFunctionParameter %13
385         %18 = OpLabel
386         %31 = OpVariable %7 Function
387         %32 = OpLoad %6 %15
388               OpStore %31 %32
389         %33 = OpFunctionCall %6 %10 %31
390               OpReturnValue %33
391               OpFunctionEnd
392         %21 = OpFunction %6 None %14
393         %19 = OpFunctionParameter %7
394         %20 = OpFunctionParameter %13
395         %22 = OpLabel
396        %102 = OpFunctionCall %6 %200 %19 %20
397         %36 = OpLoad %6 %19
398         %37 = OpLoad %12 %20
399         %38 = OpConvertFToS %6 %37
400         %39 = OpIAdd %6 %36 %38
401               OpReturnValue %39
402               OpFunctionEnd
403         %24 = OpFunction %6 None %8
404         %23 = OpFunctionParameter %7
405         %25 = OpLabel
406         %44 = OpVariable %7 Function
407         %46 = OpVariable %13 Function
408         %51 = OpVariable %7 Function
409         %52 = OpVariable %13 Function
410         %42 = OpLoad %6 %23
411         %43 = OpConvertSToF %12 %42
412        %103 = OpFunctionCall %6 %10 %23
413         %45 = OpLoad %6 %23
414               OpStore %44 %45
415               OpStore %46 %43
416         %47 = OpFunctionCall %6 %17 %44 %46
417         %48 = OpLoad %6 %23
418         %49 = OpIAdd %6 %48 %27
419               OpStore %51 %49
420               OpStore %52 %50
421         %53 = OpFunctionCall %6 %17 %51 %52
422         %54 = OpIAdd %6 %47 %53
423               OpReturnValue %54
424               OpFunctionEnd
425        %200 = OpFunction %6 None %14
426        %201 = OpFunctionParameter %7
427        %202 = OpFunctionParameter %13
428        %203 = OpLabel
429               OpSelectionMerge %206 None
430               OpBranchConditional %58 %205 %206
431        %205 = OpLabel
432        %104 = OpFunctionCall %6 %10 %201
433               OpBranch %206
434        %206 = OpLabel
435               OpReturnValue %204
436               OpFunctionEnd
437  )";
438  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
439}
440
441TEST(TransformationFunctionCallTest, DoNotInvokeEntryPoint) {
442  std::string shader = R"(
443               OpCapability Shader
444          %1 = OpExtInstImport "GLSL.std.450"
445               OpMemoryModel Logical GLSL450
446               OpEntryPoint Fragment %4 "main"
447               OpExecutionMode %4 OriginUpperLeft
448               OpSource ESSL 310
449          %2 = OpTypeVoid
450          %3 = OpTypeFunction %2
451          %4 = OpFunction %2 None %3
452          %5 = OpLabel
453               OpReturn
454               OpFunctionEnd
455         %10 = OpFunction %2 None %3
456         %11 = OpLabel
457               OpReturn
458               OpFunctionEnd
459  )";
460
461  const auto env = SPV_ENV_UNIVERSAL_1_4;
462  const auto consumer = nullptr;
463  const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
464  spvtools::ValidatorOptions validator_options;
465  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
466                                               kConsoleMessageConsumer));
467  TransformationContext transformation_context(
468      MakeUnique<FactManager>(context.get()), validator_options);
469  transformation_context.GetFactManager()->AddFactBlockIsDead(11);
470
471  // 4 is an entry point, so it is not legal for it to be the target of a call.
472  ASSERT_FALSE(
473      TransformationFunctionCall(
474          100, 4, {}, MakeInstructionDescriptor(11, spv::Op::OpReturn, 0))
475          .IsApplicable(context.get(), transformation_context));
476}
477
478}  // namespace
479}  // namespace fuzz
480}  // namespace spvtools
481