fuzzer_pass_obfuscate_constants.cpp 23 KB

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