fuzzer_pass_construct_composites.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  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_construct_composites.h"
  15. #include <cmath>
  16. #include <memory>
  17. #include "source/fuzz/fuzzer_util.h"
  18. #include "source/fuzz/transformation_composite_construct.h"
  19. #include "source/util/make_unique.h"
  20. namespace spvtools {
  21. namespace fuzz {
  22. FuzzerPassConstructComposites::FuzzerPassConstructComposites(
  23. opt::IRContext* ir_context, FactManager* fact_manager,
  24. FuzzerContext* fuzzer_context,
  25. protobufs::TransformationSequence* transformations)
  26. : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations) {}
  27. FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default;
  28. void FuzzerPassConstructComposites::Apply() {
  29. // Gather up the ids of all composite types.
  30. std::vector<uint32_t> composite_type_ids;
  31. for (auto& inst : GetIRContext()->types_values()) {
  32. if (fuzzerutil::IsCompositeType(
  33. GetIRContext()->get_type_mgr()->GetType(inst.result_id()))) {
  34. composite_type_ids.push_back(inst.result_id());
  35. }
  36. }
  37. ForEachInstructionWithInstructionDescriptor(
  38. [this, &composite_type_ids](
  39. opt::Function* function, opt::BasicBlock* block,
  40. opt::BasicBlock::iterator inst_it,
  41. const protobufs::InstructionDescriptor& instruction_descriptor)
  42. -> void {
  43. // Check whether it is legitimate to insert a composite construction
  44. // before the instruction.
  45. if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
  46. SpvOpCompositeConstruct, inst_it)) {
  47. return;
  48. }
  49. // Randomly decide whether to try inserting an object copy here.
  50. if (!GetFuzzerContext()->ChoosePercentage(
  51. GetFuzzerContext()->GetChanceOfConstructingComposite())) {
  52. return;
  53. }
  54. // For each instruction that is available at this program point (i.e. an
  55. // instruction that is global or whose definition strictly dominates the
  56. // program point) and suitable for making a synonym of, associate it
  57. // with the id of its result type.
  58. TypeIdToInstructions type_id_to_available_instructions;
  59. for (auto instruction : FindAvailableInstructions(
  60. function, block, inst_it, fuzzerutil::CanMakeSynonymOf)) {
  61. RecordAvailableInstruction(instruction,
  62. &type_id_to_available_instructions);
  63. }
  64. // At this point, |composite_type_ids| captures all the composite types
  65. // we could try to create, while |type_id_to_available_instructions|
  66. // captures all the available result ids we might use, organized by
  67. // type.
  68. // Now we try to find a composite that we can construct. We might not
  69. // manage, if there is a paucity of available ingredients in the module
  70. // (e.g. if our only available composite was a boolean vector and we had
  71. // no instructions generating boolean result types available).
  72. //
  73. // If we succeed, |chosen_composite_type| will end up being non-zero,
  74. // and |constructor_arguments| will end up giving us result ids suitable
  75. // for constructing a composite of that type. Otherwise these variables
  76. // will remain 0 and null respectively.
  77. uint32_t chosen_composite_type = 0;
  78. std::unique_ptr<std::vector<uint32_t>> constructor_arguments = nullptr;
  79. // Initially, all composite type ids are available for us to try. Keep
  80. // trying until we run out of options.
  81. auto composites_to_try_constructing = composite_type_ids;
  82. while (!composites_to_try_constructing.empty()) {
  83. // Remove a composite type from the composite types left for us to
  84. // try.
  85. auto index =
  86. GetFuzzerContext()->RandomIndex(composites_to_try_constructing);
  87. auto next_composite_to_try_constructing =
  88. composites_to_try_constructing[index];
  89. composites_to_try_constructing.erase(
  90. composites_to_try_constructing.begin() + index);
  91. // Now try to construct a composite of this type, using an appropriate
  92. // helper method depending on the kind of composite type.
  93. auto composite_type = GetIRContext()->get_type_mgr()->GetType(
  94. next_composite_to_try_constructing);
  95. if (auto array_type = composite_type->AsArray()) {
  96. constructor_arguments = TryConstructingArrayComposite(
  97. *array_type, type_id_to_available_instructions);
  98. } else if (auto matrix_type = composite_type->AsMatrix()) {
  99. constructor_arguments = TryConstructingMatrixComposite(
  100. *matrix_type, type_id_to_available_instructions);
  101. } else if (auto struct_type = composite_type->AsStruct()) {
  102. constructor_arguments = TryConstructingStructComposite(
  103. *struct_type, type_id_to_available_instructions);
  104. } else {
  105. auto vector_type = composite_type->AsVector();
  106. assert(vector_type &&
  107. "The space of possible composite types should be covered by "
  108. "the above cases.");
  109. constructor_arguments = TryConstructingVectorComposite(
  110. *vector_type, type_id_to_available_instructions);
  111. }
  112. if (constructor_arguments != nullptr) {
  113. // We succeeded! Note the composite type we finally settled on, and
  114. // exit from the loop.
  115. chosen_composite_type = next_composite_to_try_constructing;
  116. break;
  117. }
  118. }
  119. if (!chosen_composite_type) {
  120. // We did not manage to make a composite; return 0 to indicate that no
  121. // instructions were added.
  122. assert(constructor_arguments == nullptr);
  123. return;
  124. }
  125. assert(constructor_arguments != nullptr);
  126. // Make and apply a transformation.
  127. TransformationCompositeConstruct transformation(
  128. chosen_composite_type, *constructor_arguments,
  129. instruction_descriptor, GetFuzzerContext()->GetFreshId());
  130. assert(transformation.IsApplicable(GetIRContext(), *GetFactManager()) &&
  131. "This transformation should be applicable by construction.");
  132. transformation.Apply(GetIRContext(), GetFactManager());
  133. *GetTransformations()->add_transformation() =
  134. transformation.ToMessage();
  135. });
  136. }
  137. void FuzzerPassConstructComposites::RecordAvailableInstruction(
  138. opt::Instruction* inst,
  139. TypeIdToInstructions* type_id_to_available_instructions) {
  140. if (type_id_to_available_instructions->count(inst->type_id()) == 0) {
  141. (*type_id_to_available_instructions)[inst->type_id()] = {};
  142. }
  143. type_id_to_available_instructions->at(inst->type_id()).push_back(inst);
  144. }
  145. std::unique_ptr<std::vector<uint32_t>>
  146. FuzzerPassConstructComposites::TryConstructingArrayComposite(
  147. const opt::analysis::Array& array_type,
  148. const TypeIdToInstructions& type_id_to_available_instructions) {
  149. // At present we assume arrays have a constant size.
  150. assert(array_type.length_info().words.size() == 2);
  151. assert(array_type.length_info().words[0] ==
  152. opt::analysis::Array::LengthInfo::kConstant);
  153. auto result = MakeUnique<std::vector<uint32_t>>();
  154. // Get the element type for the array.
  155. auto element_type_id =
  156. GetIRContext()->get_type_mgr()->GetId(array_type.element_type());
  157. // Get all instructions at our disposal that compute something of this element
  158. // type.
  159. auto available_instructions =
  160. type_id_to_available_instructions.find(element_type_id);
  161. if (available_instructions == type_id_to_available_instructions.cend()) {
  162. // If there are not any instructions available that compute the element type
  163. // of the array then we are not in a position to construct a composite with
  164. // this array type.
  165. return nullptr;
  166. }
  167. for (uint32_t index = 0; index < array_type.length_info().words[1]; index++) {
  168. result->push_back(available_instructions
  169. ->second[GetFuzzerContext()->RandomIndex(
  170. available_instructions->second)]
  171. ->result_id());
  172. }
  173. return result;
  174. }
  175. std::unique_ptr<std::vector<uint32_t>>
  176. FuzzerPassConstructComposites::TryConstructingMatrixComposite(
  177. const opt::analysis::Matrix& matrix_type,
  178. const TypeIdToInstructions& type_id_to_available_instructions) {
  179. auto result = MakeUnique<std::vector<uint32_t>>();
  180. // Get the element type for the matrix.
  181. auto element_type_id =
  182. GetIRContext()->get_type_mgr()->GetId(matrix_type.element_type());
  183. // Get all instructions at our disposal that compute something of this element
  184. // type.
  185. auto available_instructions =
  186. type_id_to_available_instructions.find(element_type_id);
  187. if (available_instructions == type_id_to_available_instructions.cend()) {
  188. // If there are not any instructions available that compute the element type
  189. // of the matrix then we are not in a position to construct a composite with
  190. // this matrix type.
  191. return nullptr;
  192. }
  193. for (uint32_t index = 0; index < matrix_type.element_count(); index++) {
  194. result->push_back(available_instructions
  195. ->second[GetFuzzerContext()->RandomIndex(
  196. available_instructions->second)]
  197. ->result_id());
  198. }
  199. return result;
  200. }
  201. std::unique_ptr<std::vector<uint32_t>>
  202. FuzzerPassConstructComposites::TryConstructingStructComposite(
  203. const opt::analysis::Struct& struct_type,
  204. const TypeIdToInstructions& type_id_to_available_instructions) {
  205. auto result = MakeUnique<std::vector<uint32_t>>();
  206. // Consider the type of each field of the struct.
  207. for (auto element_type : struct_type.element_types()) {
  208. auto element_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
  209. // Find the instructions at our disposal that compute something of the field
  210. // type.
  211. auto available_instructions =
  212. type_id_to_available_instructions.find(element_type_id);
  213. if (available_instructions == type_id_to_available_instructions.cend()) {
  214. // If there are no such instructions, we cannot construct a composite of
  215. // this struct type.
  216. return nullptr;
  217. }
  218. result->push_back(available_instructions
  219. ->second[GetFuzzerContext()->RandomIndex(
  220. available_instructions->second)]
  221. ->result_id());
  222. }
  223. return result;
  224. }
  225. std::unique_ptr<std::vector<uint32_t>>
  226. FuzzerPassConstructComposites::TryConstructingVectorComposite(
  227. const opt::analysis::Vector& vector_type,
  228. const TypeIdToInstructions& type_id_to_available_instructions) {
  229. // Get details of the type underlying the vector, and the width of the vector,
  230. // for convenience.
  231. auto element_type = vector_type.element_type();
  232. auto element_count = vector_type.element_count();
  233. // Collect a mapping, from type id to width, for scalar/vector types that are
  234. // smaller in width than |vector_type|, but that have the same underlying
  235. // type. For example, if |vector_type| is vec4, the mapping will be:
  236. // { float -> 1, vec2 -> 2, vec3 -> 3 }
  237. // The mapping will have missing entries if some of these types do not exist.
  238. std::map<uint32_t, uint32_t> smaller_vector_type_id_to_width;
  239. // Add the underlying type. This id must exist, in order for |vector_type| to
  240. // exist.
  241. auto scalar_type_id = GetIRContext()->get_type_mgr()->GetId(element_type);
  242. smaller_vector_type_id_to_width[scalar_type_id] = 1;
  243. // Now add every vector type with width at least 2, and less than the width of
  244. // |vector_type|.
  245. for (uint32_t width = 2; width < element_count; width++) {
  246. opt::analysis::Vector smaller_vector_type(vector_type.element_type(),
  247. width);
  248. auto smaller_vector_type_id =
  249. GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type);
  250. // We might find that there is no declared type of this smaller width.
  251. // For example, a module can declare vec4 without having declared vec2 or
  252. // vec3.
  253. if (smaller_vector_type_id) {
  254. smaller_vector_type_id_to_width[smaller_vector_type_id] = width;
  255. }
  256. }
  257. // Now we know the types that are available to us, we set about populating a
  258. // vector of the right length. We do this by deciding, with no order in mind,
  259. // which instructions we will use to populate the vector, and subsequently
  260. // randomly choosing an order. This is to avoid biasing construction of
  261. // vectors with smaller vectors to the left and scalars to the right. That is
  262. // a concern because, e.g. in the case of populating a vec4, if we populate
  263. // the constructor instructions left-to-right, we can always choose a vec3 to
  264. // construct the first three elements, but can only choose a vec3 to construct
  265. // the last three elements if we chose a float to construct the first element
  266. // (otherwise there will not be space left for a vec3).
  267. uint32_t vector_slots_used = 0;
  268. // The instructions we will use to construct the vector, in no particular
  269. // order at this stage.
  270. std::vector<opt::Instruction*> instructions_to_use;
  271. while (vector_slots_used < vector_type.element_count()) {
  272. std::vector<opt::Instruction*> instructions_to_choose_from;
  273. for (auto& entry : smaller_vector_type_id_to_width) {
  274. if (entry.second >
  275. std::min(vector_type.element_count() - 1,
  276. vector_type.element_count() - vector_slots_used)) {
  277. continue;
  278. }
  279. auto available_instructions =
  280. type_id_to_available_instructions.find(entry.first);
  281. if (available_instructions == type_id_to_available_instructions.cend()) {
  282. continue;
  283. }
  284. instructions_to_choose_from.insert(instructions_to_choose_from.end(),
  285. available_instructions->second.begin(),
  286. available_instructions->second.end());
  287. }
  288. if (instructions_to_choose_from.empty()) {
  289. // We may get unlucky and find that there are not any instructions to
  290. // choose from. In this case we give up constructing a composite of this
  291. // vector type. It might be that we could construct the composite in
  292. // another manner, so we could opt to retry a few times here, but it is
  293. // simpler to just give up on the basis that this will not happen
  294. // frequently.
  295. return nullptr;
  296. }
  297. auto instruction_to_use =
  298. instructions_to_choose_from[GetFuzzerContext()->RandomIndex(
  299. instructions_to_choose_from)];
  300. instructions_to_use.push_back(instruction_to_use);
  301. auto chosen_type =
  302. GetIRContext()->get_type_mgr()->GetType(instruction_to_use->type_id());
  303. if (chosen_type->AsVector()) {
  304. assert(chosen_type->AsVector()->element_type() == element_type);
  305. assert(chosen_type->AsVector()->element_count() < element_count);
  306. assert(chosen_type->AsVector()->element_count() <=
  307. element_count - vector_slots_used);
  308. vector_slots_used += chosen_type->AsVector()->element_count();
  309. } else {
  310. assert(chosen_type == element_type);
  311. vector_slots_used += 1;
  312. }
  313. }
  314. assert(vector_slots_used == vector_type.element_count());
  315. auto result = MakeUnique<std::vector<uint32_t>>();
  316. std::vector<uint32_t> operands;
  317. while (!instructions_to_use.empty()) {
  318. auto index = GetFuzzerContext()->RandomIndex(instructions_to_use);
  319. result->push_back(instructions_to_use[index]->result_id());
  320. instructions_to_use.erase(instructions_to_use.begin() + index);
  321. }
  322. assert(result->size() > 1);
  323. return result;
  324. }
  325. } // namespace fuzz
  326. } // namespace spvtools