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