1 // Copyright (c) 2019 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/fuzzer_pass_obfuscate_constants.h"
16
17 #include <algorithm>
18 #include <cmath>
19
20 #include "source/fuzz/fuzzer_util.h"
21 #include "source/fuzz/instruction_descriptor.h"
22 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
23 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
24 #include "source/fuzz/uniform_buffer_element_descriptor.h"
25 #include "source/opt/ir_context.h"
26
27 namespace spvtools {
28 namespace fuzz {
29
FuzzerPassObfuscateConstants(opt::IRContext * ir_context,TransformationContext * transformation_context,FuzzerContext * fuzzer_context,protobufs::TransformationSequence * transformations,bool ignore_inapplicable_transformations)30 FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants(
31 opt::IRContext* ir_context, TransformationContext* transformation_context,
32 FuzzerContext* fuzzer_context,
33 protobufs::TransformationSequence* transformations,
34 bool ignore_inapplicable_transformations)
35 : FuzzerPass(ir_context, transformation_context, fuzzer_context,
36 transformations, ignore_inapplicable_transformations) {}
37
ObfuscateBoolConstantViaConstantPair(uint32_t depth,const protobufs::IdUseDescriptor & bool_constant_use,const std::vector<spv::Op> & greater_than_opcodes,const std::vector<spv::Op> & less_than_opcodes,uint32_t constant_id_1,uint32_t constant_id_2,bool first_constant_is_larger)38 void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair(
39 uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
40 const std::vector<spv::Op>& greater_than_opcodes,
41 const std::vector<spv::Op>& less_than_opcodes, uint32_t constant_id_1,
42 uint32_t constant_id_2, bool first_constant_is_larger) {
43 auto bool_constant_opcode = GetIRContext()
44 ->get_def_use_mgr()
45 ->GetDef(bool_constant_use.id_of_interest())
46 ->opcode();
47 assert((bool_constant_opcode == spv::Op::OpConstantFalse ||
48 bool_constant_opcode == spv::Op::OpConstantTrue) &&
49 "Precondition: this must be a usage of a boolean constant.");
50
51 // Pick an opcode at random. First randomly decide whether to generate
52 // a 'greater than' or 'less than' kind of opcode, and then select a
53 // random opcode from the resulting subset.
54 spv::Op comparison_opcode;
55 if (GetFuzzerContext()->ChooseEven()) {
56 comparison_opcode = greater_than_opcodes[GetFuzzerContext()->RandomIndex(
57 greater_than_opcodes)];
58 } else {
59 comparison_opcode =
60 less_than_opcodes[GetFuzzerContext()->RandomIndex(less_than_opcodes)];
61 }
62
63 // We now need to decide how to order constant_id_1 and constant_id_2 such
64 // that 'constant_id_1 comparison_opcode constant_id_2' evaluates to the
65 // boolean constant.
66 const bool is_greater_than_opcode =
67 std::find(greater_than_opcodes.begin(), greater_than_opcodes.end(),
68 comparison_opcode) != greater_than_opcodes.end();
69 uint32_t lhs_id;
70 uint32_t rhs_id;
71 if ((bool_constant_opcode == spv::Op::OpConstantTrue &&
72 first_constant_is_larger == is_greater_than_opcode) ||
73 (bool_constant_opcode == spv::Op::OpConstantFalse &&
74 first_constant_is_larger != is_greater_than_opcode)) {
75 lhs_id = constant_id_1;
76 rhs_id = constant_id_2;
77 } else {
78 lhs_id = constant_id_2;
79 rhs_id = constant_id_1;
80 }
81
82 // We can now make a transformation that will replace |bool_constant_use|
83 // with an expression of the form (written using infix notation):
84 // |lhs_id| |comparison_opcode| |rhs_id|
85 auto transformation = TransformationReplaceBooleanConstantWithConstantBinary(
86 bool_constant_use, lhs_id, rhs_id, comparison_opcode,
87 GetFuzzerContext()->GetFreshId());
88 // The transformation should be applicable by construction.
89 assert(
90 transformation.IsApplicable(GetIRContext(), *GetTransformationContext()));
91
92 // Applying this transformation yields a pointer to the new instruction that
93 // computes the result of the binary expression.
94 auto binary_operator_instruction = transformation.ApplyWithResult(
95 GetIRContext(), GetTransformationContext());
96
97 // Add this transformation to the sequence of transformations that have been
98 // applied.
99 *GetTransformations()->add_transformation() = transformation.ToMessage();
100
101 // Having made a binary expression, there may now be opportunities to further
102 // obfuscate the constants used as the LHS and RHS of the expression (e.g. by
103 // replacing them with loads from known uniforms).
104 //
105 // We thus consider operands 0 and 1 (LHS and RHS in turn).
106 for (uint32_t index : {0u, 1u}) {
107 // We randomly decide, based on the current depth of obfuscation, whether
108 // to further obfuscate this operand.
109 if (GetFuzzerContext()->GoDeeperInConstantObfuscation(depth)) {
110 auto in_operand_use = MakeIdUseDescriptor(
111 binary_operator_instruction->GetSingleWordInOperand(index),
112 MakeInstructionDescriptor(binary_operator_instruction->result_id(),
113 binary_operator_instruction->opcode(), 0),
114 index);
115 ObfuscateConstant(depth + 1, in_operand_use);
116 }
117 }
118 }
119
ObfuscateBoolConstantViaFloatConstantPair(uint32_t depth,const protobufs::IdUseDescriptor & bool_constant_use,uint32_t float_constant_id_1,uint32_t float_constant_id_2)120 void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaFloatConstantPair(
121 uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
122 uint32_t float_constant_id_1, uint32_t float_constant_id_2) {
123 auto float_constant_1 = GetIRContext()
124 ->get_constant_mgr()
125 ->FindDeclaredConstant(float_constant_id_1)
126 ->AsFloatConstant();
127 auto float_constant_2 = GetIRContext()
128 ->get_constant_mgr()
129 ->FindDeclaredConstant(float_constant_id_2)
130 ->AsFloatConstant();
131 assert(float_constant_1->words() != float_constant_2->words() &&
132 "The constants should not be identical.");
133 assert(std::isfinite(float_constant_1->GetValueAsDouble()) &&
134 "The constants must be finite numbers.");
135 assert(std::isfinite(float_constant_2->GetValueAsDouble()) &&
136 "The constants must be finite numbers.");
137 bool first_constant_is_larger;
138 assert(float_constant_1->type()->AsFloat()->width() ==
139 float_constant_2->type()->AsFloat()->width() &&
140 "First and second floating-point constants must have the same width.");
141 if (float_constant_1->type()->AsFloat()->width() == 32) {
142 first_constant_is_larger =
143 float_constant_1->GetFloat() > float_constant_2->GetFloat();
144 } else {
145 assert(float_constant_1->type()->AsFloat()->width() == 64 &&
146 "Supported floating-point widths are 32 and 64.");
147 first_constant_is_larger =
148 float_constant_1->GetDouble() > float_constant_2->GetDouble();
149 }
150 std::vector<spv::Op> greater_than_opcodes{
151 spv::Op::OpFOrdGreaterThan, spv::Op::OpFOrdGreaterThanEqual,
152 spv::Op::OpFUnordGreaterThan, spv::Op::OpFUnordGreaterThanEqual};
153 std::vector<spv::Op> less_than_opcodes{
154 spv::Op::OpFOrdGreaterThan, spv::Op::OpFOrdGreaterThanEqual,
155 spv::Op::OpFUnordGreaterThan, spv::Op::OpFUnordGreaterThanEqual};
156
157 ObfuscateBoolConstantViaConstantPair(
158 depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
159 float_constant_id_1, float_constant_id_2, first_constant_is_larger);
160 }
161
162 void FuzzerPassObfuscateConstants::
ObfuscateBoolConstantViaSignedIntConstantPair(uint32_t depth,const protobufs::IdUseDescriptor & bool_constant_use,uint32_t signed_int_constant_id_1,uint32_t signed_int_constant_id_2)163 ObfuscateBoolConstantViaSignedIntConstantPair(
164 uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
165 uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2) {
166 auto signed_int_constant_1 =
167 GetIRContext()
168 ->get_constant_mgr()
169 ->FindDeclaredConstant(signed_int_constant_id_1)
170 ->AsIntConstant();
171 auto signed_int_constant_2 =
172 GetIRContext()
173 ->get_constant_mgr()
174 ->FindDeclaredConstant(signed_int_constant_id_2)
175 ->AsIntConstant();
176 assert(signed_int_constant_1->words() != signed_int_constant_2->words() &&
177 "The constants should not be identical.");
178 bool first_constant_is_larger;
179 assert(signed_int_constant_1->type()->AsInteger()->width() ==
180 signed_int_constant_2->type()->AsInteger()->width() &&
181 "First and second floating-point constants must have the same width.");
182 assert(signed_int_constant_1->type()->AsInteger()->IsSigned());
183 assert(signed_int_constant_2->type()->AsInteger()->IsSigned());
184 if (signed_int_constant_1->type()->AsFloat()->width() == 32) {
185 first_constant_is_larger =
186 signed_int_constant_1->GetS32() > signed_int_constant_2->GetS32();
187 } else {
188 assert(signed_int_constant_1->type()->AsFloat()->width() == 64 &&
189 "Supported integer widths are 32 and 64.");
190 first_constant_is_larger =
191 signed_int_constant_1->GetS64() > signed_int_constant_2->GetS64();
192 }
193 std::vector<spv::Op> greater_than_opcodes{spv::Op::OpSGreaterThan,
194 spv::Op::OpSGreaterThanEqual};
195 std::vector<spv::Op> less_than_opcodes{spv::Op::OpSLessThan,
196 spv::Op::OpSLessThanEqual};
197
198 ObfuscateBoolConstantViaConstantPair(
199 depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
200 signed_int_constant_id_1, signed_int_constant_id_2,
201 first_constant_is_larger);
202 }
203
204 void FuzzerPassObfuscateConstants::
ObfuscateBoolConstantViaUnsignedIntConstantPair(uint32_t depth,const protobufs::IdUseDescriptor & bool_constant_use,uint32_t unsigned_int_constant_id_1,uint32_t unsigned_int_constant_id_2)205 ObfuscateBoolConstantViaUnsignedIntConstantPair(
206 uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use,
207 uint32_t unsigned_int_constant_id_1,
208 uint32_t unsigned_int_constant_id_2) {
209 auto unsigned_int_constant_1 =
210 GetIRContext()
211 ->get_constant_mgr()
212 ->FindDeclaredConstant(unsigned_int_constant_id_1)
213 ->AsIntConstant();
214 auto unsigned_int_constant_2 =
215 GetIRContext()
216 ->get_constant_mgr()
217 ->FindDeclaredConstant(unsigned_int_constant_id_2)
218 ->AsIntConstant();
219 assert(unsigned_int_constant_1->words() != unsigned_int_constant_2->words() &&
220 "The constants should not be identical.");
221 bool first_constant_is_larger;
222 assert(unsigned_int_constant_1->type()->AsInteger()->width() ==
223 unsigned_int_constant_2->type()->AsInteger()->width() &&
224 "First and second floating-point constants must have the same width.");
225 assert(!unsigned_int_constant_1->type()->AsInteger()->IsSigned());
226 assert(!unsigned_int_constant_2->type()->AsInteger()->IsSigned());
227 if (unsigned_int_constant_1->type()->AsFloat()->width() == 32) {
228 first_constant_is_larger =
229 unsigned_int_constant_1->GetU32() > unsigned_int_constant_2->GetU32();
230 } else {
231 assert(unsigned_int_constant_1->type()->AsFloat()->width() == 64 &&
232 "Supported integer widths are 32 and 64.");
233 first_constant_is_larger =
234 unsigned_int_constant_1->GetU64() > unsigned_int_constant_2->GetU64();
235 }
236 std::vector<spv::Op> greater_than_opcodes{spv::Op::OpUGreaterThan,
237 spv::Op::OpUGreaterThanEqual};
238 std::vector<spv::Op> less_than_opcodes{spv::Op::OpULessThan,
239 spv::Op::OpULessThanEqual};
240
241 ObfuscateBoolConstantViaConstantPair(
242 depth, bool_constant_use, greater_than_opcodes, less_than_opcodes,
243 unsigned_int_constant_id_1, unsigned_int_constant_id_2,
244 first_constant_is_larger);
245 }
246
247 std::vector<std::vector<uint32_t>>
GetConstantWordsFromUniformsForType(uint32_t type_id)248 FuzzerPassObfuscateConstants::GetConstantWordsFromUniformsForType(
249 uint32_t type_id) {
250 assert(type_id && "Type id can't be 0");
251 std::vector<std::vector<uint32_t>> result;
252
253 for (const auto& facts_and_types : GetTransformationContext()
254 ->GetFactManager()
255 ->GetConstantUniformFactsAndTypes()) {
256 if (facts_and_types.second != type_id) {
257 continue;
258 }
259
260 std::vector<uint32_t> words(facts_and_types.first.constant_word().begin(),
261 facts_and_types.first.constant_word().end());
262 if (std::find(result.begin(), result.end(), words) == result.end()) {
263 result.push_back(std::move(words));
264 }
265 }
266
267 return result;
268 }
269
ObfuscateBoolConstant(uint32_t depth,const protobufs::IdUseDescriptor & constant_use)270 void FuzzerPassObfuscateConstants::ObfuscateBoolConstant(
271 uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
272 // We want to replace the boolean constant use with a binary expression over
273 // scalar constants, but only if we can then potentially replace the constants
274 // with uniforms of the same value.
275
276 auto available_types_with_uniforms =
277 GetTransformationContext()
278 ->GetFactManager()
279 ->GetTypesForWhichUniformValuesAreKnown();
280 if (available_types_with_uniforms.empty()) {
281 // Do not try to obfuscate if we do not have access to any uniform
282 // elements with known values.
283 return;
284 }
285 auto chosen_type_id =
286 available_types_with_uniforms[GetFuzzerContext()->RandomIndex(
287 available_types_with_uniforms)];
288 auto available_constant_words =
289 GetConstantWordsFromUniformsForType(chosen_type_id);
290 if (available_constant_words.size() == 1) {
291 // TODO(afd): for now we only obfuscate a boolean if there are at least
292 // two constants available from uniforms, so that we can do a
293 // comparison between them. It would be good to be able to do the
294 // obfuscation even if there is only one such constant, if there is
295 // also another regular constant available.
296 return;
297 }
298
299 assert(!available_constant_words.empty() &&
300 "There exists a fact but no constants - impossible");
301
302 // We know we have at least two known-to-be-constant uniforms of the chosen
303 // type. Pick one of them at random.
304 auto constant_index_1 =
305 GetFuzzerContext()->RandomIndex(available_constant_words);
306 uint32_t constant_index_2;
307
308 // Now choose another one distinct from the first one.
309 do {
310 constant_index_2 =
311 GetFuzzerContext()->RandomIndex(available_constant_words);
312 } while (constant_index_1 == constant_index_2);
313
314 auto constant_id_1 = FindOrCreateConstant(
315 available_constant_words[constant_index_1], chosen_type_id, false);
316 auto constant_id_2 = FindOrCreateConstant(
317 available_constant_words[constant_index_2], chosen_type_id, false);
318
319 assert(constant_id_1 != 0 && constant_id_2 != 0 &&
320 "We should not find an available constant with an id of 0.");
321
322 // Now perform the obfuscation, according to whether the type of the constants
323 // is float, signed int, or unsigned int.
324 auto chosen_type = GetIRContext()->get_type_mgr()->GetType(chosen_type_id);
325 if (chosen_type->AsFloat()) {
326 ObfuscateBoolConstantViaFloatConstantPair(depth, constant_use,
327 constant_id_1, constant_id_2);
328 } else {
329 assert(chosen_type->AsInteger() &&
330 "We should only have uniform facts about ints and floats.");
331 if (chosen_type->AsInteger()->IsSigned()) {
332 ObfuscateBoolConstantViaSignedIntConstantPair(
333 depth, constant_use, constant_id_1, constant_id_2);
334 } else {
335 ObfuscateBoolConstantViaUnsignedIntConstantPair(
336 depth, constant_use, constant_id_1, constant_id_2);
337 }
338 }
339 }
340
ObfuscateScalarConstant(uint32_t,const protobufs::IdUseDescriptor & constant_use)341 void FuzzerPassObfuscateConstants::ObfuscateScalarConstant(
342 uint32_t /*depth*/, const protobufs::IdUseDescriptor& constant_use) {
343 // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2670): consider
344 // additional ways to obfuscate scalar constants.
345
346 // Check whether we know that any uniforms are guaranteed to be equal to the
347 // scalar constant associated with |constant_use|.
348 auto uniform_descriptors =
349 GetTransformationContext()
350 ->GetFactManager()
351 ->GetUniformDescriptorsForConstant(constant_use.id_of_interest());
352 if (uniform_descriptors.empty()) {
353 // No relevant uniforms, so do not obfuscate.
354 return;
355 }
356
357 // Choose a random available uniform known to be equal to the constant.
358 const auto& uniform_descriptor =
359 uniform_descriptors[GetFuzzerContext()->RandomIndex(uniform_descriptors)];
360
361 // Make sure the module has OpConstant instructions for each index used to
362 // access a uniform.
363 for (auto index : uniform_descriptor.index()) {
364 FindOrCreateIntegerConstant({index}, 32, true, false);
365 }
366
367 // Make sure the module has OpTypePointer that points to the element type of
368 // the uniform.
369 const auto* uniform_variable_instr =
370 FindUniformVariable(uniform_descriptor, GetIRContext(), true);
371 assert(uniform_variable_instr &&
372 "Uniform variable does not exist or not unique.");
373
374 const auto* uniform_variable_type_intr =
375 GetIRContext()->get_def_use_mgr()->GetDef(
376 uniform_variable_instr->type_id());
377 assert(uniform_variable_type_intr && "Uniform variable has invalid type");
378
379 auto element_type_id = fuzzerutil::WalkCompositeTypeIndices(
380 GetIRContext(), uniform_variable_type_intr->GetSingleWordInOperand(1),
381 uniform_descriptor.index());
382 assert(element_type_id && "Type of uniform variable is invalid");
383
384 FindOrCreatePointerType(element_type_id, spv::StorageClass::Uniform);
385
386 // Create, apply and record a transformation to replace the constant use with
387 // the result of a load from the chosen uniform.
388 ApplyTransformation(TransformationReplaceConstantWithUniform(
389 constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(),
390 GetFuzzerContext()->GetFreshId()));
391 }
392
ObfuscateConstant(uint32_t depth,const protobufs::IdUseDescriptor & constant_use)393 void FuzzerPassObfuscateConstants::ObfuscateConstant(
394 uint32_t depth, const protobufs::IdUseDescriptor& constant_use) {
395 switch (GetIRContext()
396 ->get_def_use_mgr()
397 ->GetDef(constant_use.id_of_interest())
398 ->opcode()) {
399 case spv::Op::OpConstantTrue:
400 case spv::Op::OpConstantFalse:
401 ObfuscateBoolConstant(depth, constant_use);
402 break;
403 case spv::Op::OpConstant:
404 ObfuscateScalarConstant(depth, constant_use);
405 break;
406 default:
407 assert(false && "The opcode should be one of the above.");
408 break;
409 }
410 }
411
MaybeAddConstantIdUse(const opt::Instruction & inst,uint32_t in_operand_index,uint32_t base_instruction_result_id,const std::map<spv::Op,uint32_t> & skipped_opcode_count,std::vector<protobufs::IdUseDescriptor> * constant_uses)412 void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse(
413 const opt::Instruction& inst, uint32_t in_operand_index,
414 uint32_t base_instruction_result_id,
415 const std::map<spv::Op, uint32_t>& skipped_opcode_count,
416 std::vector<protobufs::IdUseDescriptor>* constant_uses) {
417 if (inst.GetInOperand(in_operand_index).type != SPV_OPERAND_TYPE_ID) {
418 // The operand is not an id, so it cannot be a constant id.
419 return;
420 }
421 auto operand_id = inst.GetSingleWordInOperand(in_operand_index);
422 auto operand_definition =
423 GetIRContext()->get_def_use_mgr()->GetDef(operand_id);
424 switch (operand_definition->opcode()) {
425 case spv::Op::OpConstantFalse:
426 case spv::Op::OpConstantTrue:
427 case spv::Op::OpConstant: {
428 // The operand is a constant id, so make an id use descriptor and record
429 // it.
430 protobufs::IdUseDescriptor id_use_descriptor;
431 id_use_descriptor.set_id_of_interest(operand_id);
432 id_use_descriptor.mutable_enclosing_instruction()
433 ->set_target_instruction_opcode(uint32_t(inst.opcode()));
434 id_use_descriptor.mutable_enclosing_instruction()
435 ->set_base_instruction_result_id(base_instruction_result_id);
436 id_use_descriptor.mutable_enclosing_instruction()
437 ->set_num_opcodes_to_ignore(
438 skipped_opcode_count.find(inst.opcode()) ==
439 skipped_opcode_count.end()
440 ? 0
441 : skipped_opcode_count.at(inst.opcode()));
442 id_use_descriptor.set_in_operand_index(in_operand_index);
443 constant_uses->push_back(id_use_descriptor);
444 } break;
445 default:
446 break;
447 }
448 }
449
Apply()450 void FuzzerPassObfuscateConstants::Apply() {
451 // First, gather up all the constant uses available in the module, by going
452 // through each block in each function.
453 std::vector<protobufs::IdUseDescriptor> constant_uses;
454 for (auto& function : *GetIRContext()->module()) {
455 for (auto& block : function) {
456 // For each constant use we encounter we are going to make an id use
457 // descriptor. An id use is described with respect to a base instruction;
458 // if there are instructions at the start of the block without result ids,
459 // the base instruction will have to be the block's label.
460 uint32_t base_instruction_result_id = block.id();
461
462 // An id use descriptor also records how many instructions of a particular
463 // opcode need to be skipped in order to find the instruction of interest
464 // from the base instruction. We maintain a mapping that records a skip
465 // count for each relevant opcode.
466 std::map<spv::Op, uint32_t> skipped_opcode_count;
467
468 // Go through each instruction in the block.
469 for (auto& inst : block) {
470 if (inst.HasResultId()) {
471 // The instruction has a result id, so can be used as the base
472 // instruction from now on, until another instruction with a result id
473 // is encountered.
474 base_instruction_result_id = inst.result_id();
475 // Opcode skip counts were with respect to the previous base
476 // instruction and are now irrelevant.
477 skipped_opcode_count.clear();
478 }
479
480 // The instruction must not be an OpVariable, the only id that an
481 // OpVariable uses is an initializer id, which has to remain
482 // constant.
483 if (inst.opcode() != spv::Op::OpVariable) {
484 // Consider each operand of the instruction, and add a constant id
485 // use for the operand if relevant.
486 for (uint32_t in_operand_index = 0;
487 in_operand_index < inst.NumInOperands(); in_operand_index++) {
488 MaybeAddConstantIdUse(inst, in_operand_index,
489 base_instruction_result_id,
490 skipped_opcode_count, &constant_uses);
491 }
492 }
493
494 if (!inst.HasResultId()) {
495 // The instruction has no result id, so in order to identify future id
496 // uses for instructions with this opcode from the existing base
497 // instruction, we need to increase the skip count for this opcode.
498 skipped_opcode_count[inst.opcode()] =
499 skipped_opcode_count.find(inst.opcode()) ==
500 skipped_opcode_count.end()
501 ? 1
502 : skipped_opcode_count[inst.opcode()] + 1;
503 }
504 }
505 }
506 }
507
508 // Go through the constant uses in a random order by repeatedly pulling out a
509 // constant use at a random index.
510 while (!constant_uses.empty()) {
511 auto index = GetFuzzerContext()->RandomIndex(constant_uses);
512 auto constant_use = std::move(constant_uses[index]);
513 constant_uses.erase(constant_uses.begin() + index);
514 // Decide probabilistically whether to skip or obfuscate this constant use.
515 if (!GetFuzzerContext()->ChoosePercentage(
516 GetFuzzerContext()->GetChanceOfObfuscatingConstant())) {
517 continue;
518 }
519 ObfuscateConstant(0, constant_use);
520 }
521 }
522
523 } // namespace fuzz
524 } // namespace spvtools
525