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 
22 namespace spvtools {
23 namespace fuzz {
24 namespace {
25 
TEST(TransformationFunctionCallTest, BasicTest)26 TEST(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 
TEST(TransformationFunctionCallTest, DoNotInvokeEntryPoint)441 TEST(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