fuzzer_pass_donate_modules.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  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_donate_modules.h"
  15. #include <map>
  16. #include <queue>
  17. #include <set>
  18. #include "source/fuzz/call_graph.h"
  19. #include "source/fuzz/instruction_message.h"
  20. #include "source/fuzz/transformation_add_constant_boolean.h"
  21. #include "source/fuzz/transformation_add_constant_composite.h"
  22. #include "source/fuzz/transformation_add_constant_scalar.h"
  23. #include "source/fuzz/transformation_add_function.h"
  24. #include "source/fuzz/transformation_add_global_undef.h"
  25. #include "source/fuzz/transformation_add_global_variable.h"
  26. #include "source/fuzz/transformation_add_type_array.h"
  27. #include "source/fuzz/transformation_add_type_boolean.h"
  28. #include "source/fuzz/transformation_add_type_float.h"
  29. #include "source/fuzz/transformation_add_type_function.h"
  30. #include "source/fuzz/transformation_add_type_int.h"
  31. #include "source/fuzz/transformation_add_type_matrix.h"
  32. #include "source/fuzz/transformation_add_type_pointer.h"
  33. #include "source/fuzz/transformation_add_type_struct.h"
  34. #include "source/fuzz/transformation_add_type_vector.h"
  35. namespace spvtools {
  36. namespace fuzz {
  37. FuzzerPassDonateModules::FuzzerPassDonateModules(
  38. opt::IRContext* ir_context, FactManager* fact_manager,
  39. FuzzerContext* fuzzer_context,
  40. protobufs::TransformationSequence* transformations,
  41. const std::vector<fuzzerutil::ModuleSupplier>& donor_suppliers)
  42. : FuzzerPass(ir_context, fact_manager, fuzzer_context, transformations),
  43. donor_suppliers_(donor_suppliers) {}
  44. FuzzerPassDonateModules::~FuzzerPassDonateModules() = default;
  45. void FuzzerPassDonateModules::Apply() {
  46. // If there are no donor suppliers, this fuzzer pass is a no-op.
  47. if (donor_suppliers_.empty()) {
  48. return;
  49. }
  50. // Donate at least one module, and probabilistically decide when to stop
  51. // donating modules.
  52. do {
  53. // Choose a donor supplier at random, and get the module that it provides.
  54. std::unique_ptr<opt::IRContext> donor_ir_context = donor_suppliers_.at(
  55. GetFuzzerContext()->RandomIndex(donor_suppliers_))();
  56. assert(donor_ir_context != nullptr && "Supplying of donor failed");
  57. assert(fuzzerutil::IsValid(donor_ir_context.get()) &&
  58. "The donor module must be valid");
  59. // Donate the supplied module.
  60. //
  61. // Randomly decide whether to make the module livesafe (see
  62. // FactFunctionIsLivesafe); doing so allows it to be used for live code
  63. // injection but restricts its behaviour to allow this, and means that its
  64. // functions cannot be transformed as if they were arbitrary dead code.
  65. bool make_livesafe = GetFuzzerContext()->ChoosePercentage(
  66. GetFuzzerContext()->ChanceOfMakingDonorLivesafe());
  67. DonateSingleModule(donor_ir_context.get(), make_livesafe);
  68. } while (GetFuzzerContext()->ChoosePercentage(
  69. GetFuzzerContext()->GetChanceOfDonatingAdditionalModule()));
  70. }
  71. void FuzzerPassDonateModules::DonateSingleModule(
  72. opt::IRContext* donor_ir_context, bool make_livesafe) {
  73. // The ids used by the donor module may very well clash with ids defined in
  74. // the recipient module. Furthermore, some instructions defined in the donor
  75. // module will be equivalent to instructions defined in the recipient module,
  76. // and it is not always legal to re-declare equivalent instructions. For
  77. // example, OpTypeVoid cannot be declared twice.
  78. //
  79. // To handle this, we maintain a mapping from an id used in the donor module
  80. // to the corresponding id that will be used by the donated code when it
  81. // appears in the recipient module.
  82. //
  83. // This mapping is populated in two ways:
  84. // (1) by mapping a donor instruction's result id to the id of some equivalent
  85. // existing instruction in the recipient (e.g. this has to be done for
  86. // OpTypeVoid)
  87. // (2) by mapping a donor instruction's result id to a freshly chosen id that
  88. // is guaranteed to be different from any id already used by the recipient
  89. // (or from any id already chosen to handle a previous donor id)
  90. std::map<uint32_t, uint32_t> original_id_to_donated_id;
  91. HandleExternalInstructionImports(donor_ir_context,
  92. &original_id_to_donated_id);
  93. HandleTypesAndValues(donor_ir_context, &original_id_to_donated_id);
  94. HandleFunctions(donor_ir_context, &original_id_to_donated_id, make_livesafe);
  95. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3115) Handle some
  96. // kinds of decoration.
  97. }
  98. SpvStorageClass FuzzerPassDonateModules::AdaptStorageClass(
  99. SpvStorageClass donor_storage_class) {
  100. switch (donor_storage_class) {
  101. case SpvStorageClassFunction:
  102. case SpvStorageClassPrivate:
  103. // We leave these alone
  104. return donor_storage_class;
  105. case SpvStorageClassInput:
  106. case SpvStorageClassOutput:
  107. case SpvStorageClassUniform:
  108. case SpvStorageClassUniformConstant:
  109. case SpvStorageClassPushConstant:
  110. // We change these to Private
  111. return SpvStorageClassPrivate;
  112. default:
  113. // Handle other cases on demand.
  114. assert(false && "Currently unsupported storage class.");
  115. return SpvStorageClassMax;
  116. }
  117. }
  118. void FuzzerPassDonateModules::HandleExternalInstructionImports(
  119. opt::IRContext* donor_ir_context,
  120. std::map<uint32_t, uint32_t>* original_id_to_donated_id) {
  121. // Consider every external instruction set import in the donor module.
  122. for (auto& donor_import : donor_ir_context->module()->ext_inst_imports()) {
  123. const auto& donor_import_name_words = donor_import.GetInOperand(0).words;
  124. // Look for an identical import in the recipient module.
  125. for (auto& existing_import : GetIRContext()->module()->ext_inst_imports()) {
  126. const auto& existing_import_name_words =
  127. existing_import.GetInOperand(0).words;
  128. if (donor_import_name_words == existing_import_name_words) {
  129. // A matching import has found. Map the result id for the donor import
  130. // to the id of the existing import, so that when donor instructions
  131. // rely on the import they will be rewritten to use the existing import.
  132. original_id_to_donated_id->insert(
  133. {donor_import.result_id(), existing_import.result_id()});
  134. break;
  135. }
  136. }
  137. // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3116): At present
  138. // we do not handle donation of instruction imports, i.e. we do not allow
  139. // the donor to import instruction sets that the recipient did not already
  140. // import. It might be a good idea to allow this, but it requires some
  141. // thought.
  142. assert(original_id_to_donated_id->count(donor_import.result_id()) &&
  143. "Donation of imports is not yet supported.");
  144. }
  145. }
  146. void FuzzerPassDonateModules::HandleTypesAndValues(
  147. opt::IRContext* donor_ir_context,
  148. std::map<uint32_t, uint32_t>* original_id_to_donated_id) {
  149. // Consider every type/global/constant/undef in the module.
  150. for (auto& type_or_value : donor_ir_context->module()->types_values()) {
  151. // Each such instruction generates a result id, and as part of donation we
  152. // need to associate the donor's result id with a new result id. That new
  153. // result id will either be the id of some existing instruction, or a fresh
  154. // id. This variable captures it.
  155. uint32_t new_result_id;
  156. // Decide how to handle each kind of instruction on a case-by-case basis.
  157. //
  158. // Because the donor module is required to be valid, when we encounter a
  159. // type comprised of component types (e.g. an aggregate or pointer), we know
  160. // that its component types will have been considered previously, and that
  161. // |original_id_to_donated_id| will already contain an entry for them.
  162. switch (type_or_value.opcode()) {
  163. case SpvOpTypeVoid: {
  164. // Void has to exist already in order for us to have an entry point.
  165. // Get the existing id of void.
  166. opt::analysis::Void void_type;
  167. new_result_id = GetIRContext()->get_type_mgr()->GetId(&void_type);
  168. assert(new_result_id &&
  169. "The module being transformed will always have 'void' type "
  170. "declared.");
  171. } break;
  172. case SpvOpTypeBool: {
  173. // Bool cannot be declared multiple times, so use its existing id if
  174. // present, or add a declaration of Bool with a fresh id if not.
  175. opt::analysis::Bool bool_type;
  176. auto bool_type_id = GetIRContext()->get_type_mgr()->GetId(&bool_type);
  177. if (bool_type_id) {
  178. new_result_id = bool_type_id;
  179. } else {
  180. new_result_id = GetFuzzerContext()->GetFreshId();
  181. ApplyTransformation(TransformationAddTypeBoolean(new_result_id));
  182. }
  183. } break;
  184. case SpvOpTypeInt: {
  185. // Int cannot be declared multiple times with the same width and
  186. // signedness, so check whether an existing identical Int type is
  187. // present and use its id if so. Otherwise add a declaration of the
  188. // Int type used by the donor, with a fresh id.
  189. const uint32_t width = type_or_value.GetSingleWordInOperand(0);
  190. const bool is_signed =
  191. static_cast<bool>(type_or_value.GetSingleWordInOperand(1));
  192. opt::analysis::Integer int_type(width, is_signed);
  193. auto int_type_id = GetIRContext()->get_type_mgr()->GetId(&int_type);
  194. if (int_type_id) {
  195. new_result_id = int_type_id;
  196. } else {
  197. new_result_id = GetFuzzerContext()->GetFreshId();
  198. ApplyTransformation(
  199. TransformationAddTypeInt(new_result_id, width, is_signed));
  200. }
  201. } break;
  202. case SpvOpTypeFloat: {
  203. // Similar to SpvOpTypeInt.
  204. const uint32_t width = type_or_value.GetSingleWordInOperand(0);
  205. opt::analysis::Float float_type(width);
  206. auto float_type_id = GetIRContext()->get_type_mgr()->GetId(&float_type);
  207. if (float_type_id) {
  208. new_result_id = float_type_id;
  209. } else {
  210. new_result_id = GetFuzzerContext()->GetFreshId();
  211. ApplyTransformation(TransformationAddTypeFloat(new_result_id, width));
  212. }
  213. } break;
  214. case SpvOpTypeVector: {
  215. // It is not legal to have two Vector type declarations with identical
  216. // element types and element counts, so check whether an existing
  217. // identical Vector type is present and use its id if so. Otherwise add
  218. // a declaration of the Vector type used by the donor, with a fresh id.
  219. // When considering the vector's component type id, we look up the id
  220. // use in the donor to find the id to which this has been remapped.
  221. uint32_t component_type_id = original_id_to_donated_id->at(
  222. type_or_value.GetSingleWordInOperand(0));
  223. auto component_type =
  224. GetIRContext()->get_type_mgr()->GetType(component_type_id);
  225. assert(component_type && "The base type should be registered.");
  226. auto component_count = type_or_value.GetSingleWordInOperand(1);
  227. opt::analysis::Vector vector_type(component_type, component_count);
  228. auto vector_type_id =
  229. GetIRContext()->get_type_mgr()->GetId(&vector_type);
  230. if (vector_type_id) {
  231. new_result_id = vector_type_id;
  232. } else {
  233. new_result_id = GetFuzzerContext()->GetFreshId();
  234. ApplyTransformation(TransformationAddTypeVector(
  235. new_result_id, component_type_id, component_count));
  236. }
  237. } break;
  238. case SpvOpTypeMatrix: {
  239. // Similar to SpvOpTypeVector.
  240. uint32_t column_type_id = original_id_to_donated_id->at(
  241. type_or_value.GetSingleWordInOperand(0));
  242. auto column_type =
  243. GetIRContext()->get_type_mgr()->GetType(column_type_id);
  244. assert(column_type && column_type->AsVector() &&
  245. "The column type should be a registered vector type.");
  246. auto column_count = type_or_value.GetSingleWordInOperand(1);
  247. opt::analysis::Matrix matrix_type(column_type, column_count);
  248. auto matrix_type_id =
  249. GetIRContext()->get_type_mgr()->GetId(&matrix_type);
  250. if (matrix_type_id) {
  251. new_result_id = matrix_type_id;
  252. } else {
  253. new_result_id = GetFuzzerContext()->GetFreshId();
  254. ApplyTransformation(TransformationAddTypeMatrix(
  255. new_result_id, column_type_id, column_count));
  256. }
  257. } break;
  258. case SpvOpTypeArray: {
  259. // It is OK to have multiple structurally identical array types, so
  260. // we go ahead and add a remapped version of the type declared by the
  261. // donor.
  262. new_result_id = GetFuzzerContext()->GetFreshId();
  263. ApplyTransformation(TransformationAddTypeArray(
  264. new_result_id,
  265. original_id_to_donated_id->at(
  266. type_or_value.GetSingleWordInOperand(0)),
  267. original_id_to_donated_id->at(
  268. type_or_value.GetSingleWordInOperand(1))));
  269. } break;
  270. case SpvOpTypeStruct: {
  271. // Similar to SpvOpTypeArray.
  272. new_result_id = GetFuzzerContext()->GetFreshId();
  273. std::vector<uint32_t> member_type_ids;
  274. type_or_value.ForEachInId(
  275. [&member_type_ids,
  276. &original_id_to_donated_id](const uint32_t* component_type_id) {
  277. member_type_ids.push_back(
  278. original_id_to_donated_id->at(*component_type_id));
  279. });
  280. ApplyTransformation(
  281. TransformationAddTypeStruct(new_result_id, member_type_ids));
  282. } break;
  283. case SpvOpTypePointer: {
  284. // Similar to SpvOpTypeArray.
  285. new_result_id = GetFuzzerContext()->GetFreshId();
  286. ApplyTransformation(TransformationAddTypePointer(
  287. new_result_id,
  288. AdaptStorageClass(static_cast<SpvStorageClass>(
  289. type_or_value.GetSingleWordInOperand(0))),
  290. original_id_to_donated_id->at(
  291. type_or_value.GetSingleWordInOperand(1))));
  292. } break;
  293. case SpvOpTypeFunction: {
  294. // It is not OK to have multiple function types that use identical ids
  295. // for their return and parameter types. We thus go through all
  296. // existing function types to look for a match. We do not use the
  297. // type manager here because we want to regard two function types that
  298. // are structurally identical but that differ with respect to the
  299. // actual ids used for pointer types as different.
  300. //
  301. // Example:
  302. //
  303. // %1 = OpTypeVoid
  304. // %2 = OpTypeInt 32 0
  305. // %3 = OpTypePointer Function %2
  306. // %4 = OpTypePointer Function %2
  307. // %5 = OpTypeFunction %1 %3
  308. // %6 = OpTypeFunction %1 %4
  309. //
  310. // We regard %5 and %6 as distinct function types here, even though
  311. // they both have the form "uint32* -> void"
  312. std::vector<uint32_t> return_and_parameter_types;
  313. for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) {
  314. return_and_parameter_types.push_back(original_id_to_donated_id->at(
  315. type_or_value.GetSingleWordInOperand(i)));
  316. }
  317. uint32_t existing_function_id = fuzzerutil::FindFunctionType(
  318. GetIRContext(), return_and_parameter_types);
  319. if (existing_function_id) {
  320. new_result_id = existing_function_id;
  321. } else {
  322. // No match was found, so add a remapped version of the function type
  323. // to the module, with a fresh id.
  324. new_result_id = GetFuzzerContext()->GetFreshId();
  325. std::vector<uint32_t> argument_type_ids;
  326. for (uint32_t i = 1; i < type_or_value.NumInOperands(); i++) {
  327. argument_type_ids.push_back(original_id_to_donated_id->at(
  328. type_or_value.GetSingleWordInOperand(i)));
  329. }
  330. ApplyTransformation(TransformationAddTypeFunction(
  331. new_result_id,
  332. original_id_to_donated_id->at(
  333. type_or_value.GetSingleWordInOperand(0)),
  334. argument_type_ids));
  335. }
  336. } break;
  337. case SpvOpConstantTrue:
  338. case SpvOpConstantFalse: {
  339. // It is OK to have duplicate definitions of True and False, so add
  340. // these to the module, using a remapped Bool type.
  341. new_result_id = GetFuzzerContext()->GetFreshId();
  342. ApplyTransformation(TransformationAddConstantBoolean(
  343. new_result_id, type_or_value.opcode() == SpvOpConstantTrue));
  344. } break;
  345. case SpvOpConstant: {
  346. // It is OK to have duplicate constant definitions, so add this to the
  347. // module using a remapped result type.
  348. new_result_id = GetFuzzerContext()->GetFreshId();
  349. std::vector<uint32_t> data_words;
  350. type_or_value.ForEachInOperand(
  351. [&data_words](const uint32_t* in_operand) {
  352. data_words.push_back(*in_operand);
  353. });
  354. ApplyTransformation(TransformationAddConstantScalar(
  355. new_result_id,
  356. original_id_to_donated_id->at(type_or_value.type_id()),
  357. data_words));
  358. } break;
  359. case SpvOpConstantComposite: {
  360. // It is OK to have duplicate constant composite definitions, so add
  361. // this to the module using remapped versions of all consituent ids and
  362. // the result type.
  363. new_result_id = GetFuzzerContext()->GetFreshId();
  364. std::vector<uint32_t> constituent_ids;
  365. type_or_value.ForEachInId(
  366. [&constituent_ids,
  367. &original_id_to_donated_id](const uint32_t* constituent_id) {
  368. constituent_ids.push_back(
  369. original_id_to_donated_id->at(*constituent_id));
  370. });
  371. ApplyTransformation(TransformationAddConstantComposite(
  372. new_result_id,
  373. original_id_to_donated_id->at(type_or_value.type_id()),
  374. constituent_ids));
  375. } break;
  376. case SpvOpVariable: {
  377. // This is a global variable that could have one of various storage
  378. // classes. However, we change all global variable pointer storage
  379. // classes (such as Uniform, Input and Output) to private when donating
  380. // pointer types. Thus this variable's pointer type is guaranteed to
  381. // have storage class private. As a result, we simply add a Private
  382. // storage class global variable, using remapped versions of the result
  383. // type and initializer ids for the global variable in the donor.
  384. //
  385. // We regard the added variable as having an irrelevant value. This
  386. // means that future passes can add stores to the variable in any
  387. // way they wish, and pass them as pointer parameters to functions
  388. // without worrying about whether their data might get modified.
  389. new_result_id = GetFuzzerContext()->GetFreshId();
  390. uint32_t remapped_pointer_type =
  391. original_id_to_donated_id->at(type_or_value.type_id());
  392. uint32_t initializer_id;
  393. if (type_or_value.NumInOperands() == 1) {
  394. // The variable did not have an initializer; initialize it to zero.
  395. // This is to limit problems associated with uninitialized data.
  396. initializer_id = FindOrCreateZeroConstant(
  397. fuzzerutil::GetPointeeTypeIdFromPointerType(
  398. GetIRContext(), remapped_pointer_type));
  399. } else {
  400. // The variable already had an initializer; use its remapped id.
  401. initializer_id = original_id_to_donated_id->at(
  402. type_or_value.GetSingleWordInOperand(1));
  403. }
  404. ApplyTransformation(TransformationAddGlobalVariable(
  405. new_result_id, remapped_pointer_type, initializer_id, true));
  406. } break;
  407. case SpvOpUndef: {
  408. // It is fine to have multiple Undef instructions of the same type, so
  409. // we just add this to the recipient module.
  410. new_result_id = GetFuzzerContext()->GetFreshId();
  411. ApplyTransformation(TransformationAddGlobalUndef(
  412. new_result_id,
  413. original_id_to_donated_id->at(type_or_value.type_id())));
  414. } break;
  415. default: {
  416. assert(0 && "Unknown type/value.");
  417. new_result_id = 0;
  418. } break;
  419. }
  420. // Update the id mapping to associate the instruction's result id with its
  421. // corresponding id in the recipient.
  422. original_id_to_donated_id->insert(
  423. {type_or_value.result_id(), new_result_id});
  424. }
  425. }
  426. void FuzzerPassDonateModules::HandleFunctions(
  427. opt::IRContext* donor_ir_context,
  428. std::map<uint32_t, uint32_t>* original_id_to_donated_id,
  429. bool make_livesafe) {
  430. // Get the ids of functions in the donor module, topologically sorted
  431. // according to the donor's call graph.
  432. auto topological_order =
  433. GetFunctionsInCallGraphTopologicalOrder(donor_ir_context);
  434. // Donate the functions in reverse topological order. This ensures that a
  435. // function gets donated before any function that depends on it. This allows
  436. // donation of the functions to be separated into a number of transformations,
  437. // each adding one function, such that every prefix of transformations leaves
  438. // the module valid.
  439. for (auto function_id = topological_order.rbegin();
  440. function_id != topological_order.rend(); ++function_id) {
  441. // Find the function to be donated.
  442. opt::Function* function_to_donate = nullptr;
  443. for (auto& function : *donor_ir_context->module()) {
  444. if (function.result_id() == *function_id) {
  445. function_to_donate = &function;
  446. break;
  447. }
  448. }
  449. assert(function_to_donate && "Function to be donated was not found.");
  450. // We will collect up protobuf messages representing the donor function's
  451. // instructions here, and use them to create an AddFunction transformation.
  452. std::vector<protobufs::Instruction> donated_instructions;
  453. // Scan through the function, remapping each result id that it generates to
  454. // a fresh id. This is necessary because functions include forward
  455. // references, e.g. to labels.
  456. function_to_donate->ForEachInst([this, &original_id_to_donated_id](
  457. const opt::Instruction* instruction) {
  458. if (instruction->result_id()) {
  459. original_id_to_donated_id->insert(
  460. {instruction->result_id(), GetFuzzerContext()->GetFreshId()});
  461. }
  462. });
  463. // Consider every instruction of the donor function.
  464. function_to_donate->ForEachInst([this, &donated_instructions,
  465. &original_id_to_donated_id](
  466. const opt::Instruction* instruction) {
  467. // Get the instruction's input operands into donation-ready form,
  468. // remapping any id uses in the process.
  469. opt::Instruction::OperandList input_operands;
  470. // Consider each input operand in turn.
  471. for (uint32_t in_operand_index = 0;
  472. in_operand_index < instruction->NumInOperands();
  473. in_operand_index++) {
  474. std::vector<uint32_t> operand_data;
  475. const opt::Operand& in_operand =
  476. instruction->GetInOperand(in_operand_index);
  477. switch (in_operand.type) {
  478. case SPV_OPERAND_TYPE_ID:
  479. case SPV_OPERAND_TYPE_TYPE_ID:
  480. case SPV_OPERAND_TYPE_RESULT_ID:
  481. case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
  482. case SPV_OPERAND_TYPE_SCOPE_ID:
  483. // This is an id operand - it consists of a single word of data,
  484. // which needs to be remapped so that it is replaced with the
  485. // donated form of the id.
  486. operand_data.push_back(
  487. original_id_to_donated_id->at(in_operand.words[0]));
  488. break;
  489. default:
  490. // For non-id operands, we just add each of the data words.
  491. for (auto word : in_operand.words) {
  492. operand_data.push_back(word);
  493. }
  494. break;
  495. }
  496. input_operands.push_back({in_operand.type, operand_data});
  497. }
  498. if (instruction->opcode() == SpvOpVariable &&
  499. instruction->NumInOperands() == 1) {
  500. // This is an uninitialized local variable. Initialize it to zero.
  501. input_operands.push_back(
  502. {SPV_OPERAND_TYPE_ID,
  503. {FindOrCreateZeroConstant(
  504. fuzzerutil::GetPointeeTypeIdFromPointerType(
  505. GetIRContext(),
  506. original_id_to_donated_id->at(instruction->type_id())))}});
  507. }
  508. // Remap the result type and result id (if present) of the
  509. // instruction, and turn it into a protobuf message.
  510. donated_instructions.push_back(MakeInstructionMessage(
  511. instruction->opcode(),
  512. instruction->type_id()
  513. ? original_id_to_donated_id->at(instruction->type_id())
  514. : 0,
  515. instruction->result_id()
  516. ? original_id_to_donated_id->at(instruction->result_id())
  517. : 0,
  518. input_operands));
  519. });
  520. if (make_livesafe) {
  521. // Various types and constants must be in place for a function to be made
  522. // live-safe. Add them if not already present.
  523. FindOrCreateBoolType(); // Needed for comparisons
  524. FindOrCreatePointerTo32BitIntegerType(
  525. false, SpvStorageClassFunction); // Needed for adding loop limiters
  526. FindOrCreate32BitIntegerConstant(
  527. 0, false); // Needed for initializing loop limiters
  528. FindOrCreate32BitIntegerConstant(
  529. 1, false); // Needed for incrementing loop limiters
  530. // Get a fresh id for the variable that will be used as a loop limiter.
  531. const uint32_t loop_limiter_variable_id =
  532. GetFuzzerContext()->GetFreshId();
  533. // Choose a random loop limit, and add the required constant to the
  534. // module if not already there.
  535. const uint32_t loop_limit = FindOrCreate32BitIntegerConstant(
  536. GetFuzzerContext()->GetRandomLoopLimit(), false);
  537. // Consider every loop header in the function to donate, and create a
  538. // structure capturing the ids to be used for manipulating the loop
  539. // limiter each time the loop is iterated.
  540. std::vector<protobufs::LoopLimiterInfo> loop_limiters;
  541. for (auto& block : *function_to_donate) {
  542. if (block.IsLoopHeader()) {
  543. protobufs::LoopLimiterInfo loop_limiter;
  544. // Grab the loop header's id, mapped to its donated value.
  545. loop_limiter.set_loop_header_id(
  546. original_id_to_donated_id->at(block.id()));
  547. // Get fresh ids that will be used to load the loop limiter, increment
  548. // it, compare it with the loop limit, and an id for a new block that
  549. // will contain the loop's original terminator.
  550. loop_limiter.set_load_id(GetFuzzerContext()->GetFreshId());
  551. loop_limiter.set_increment_id(GetFuzzerContext()->GetFreshId());
  552. loop_limiter.set_compare_id(GetFuzzerContext()->GetFreshId());
  553. loop_limiter.set_logical_op_id(GetFuzzerContext()->GetFreshId());
  554. loop_limiters.emplace_back(loop_limiter);
  555. }
  556. }
  557. // Consider every access chain in the function to donate, and create a
  558. // structure containing the ids necessary to clamp the access chain
  559. // indices to be in-bounds.
  560. std::vector<protobufs::AccessChainClampingInfo>
  561. access_chain_clamping_info;
  562. for (auto& block : *function_to_donate) {
  563. for (auto& inst : block) {
  564. switch (inst.opcode()) {
  565. case SpvOpAccessChain:
  566. case SpvOpInBoundsAccessChain: {
  567. protobufs::AccessChainClampingInfo clamping_info;
  568. clamping_info.set_access_chain_id(
  569. original_id_to_donated_id->at(inst.result_id()));
  570. auto base_object = donor_ir_context->get_def_use_mgr()->GetDef(
  571. inst.GetSingleWordInOperand(0));
  572. assert(base_object && "The base object must exist.");
  573. auto pointer_type = donor_ir_context->get_def_use_mgr()->GetDef(
  574. base_object->type_id());
  575. assert(pointer_type &&
  576. pointer_type->opcode() == SpvOpTypePointer &&
  577. "The base object must have pointer type.");
  578. auto should_be_composite_type =
  579. donor_ir_context->get_def_use_mgr()->GetDef(
  580. pointer_type->GetSingleWordInOperand(1));
  581. // Walk the access chain, creating fresh ids to facilitate
  582. // clamping each index. For simplicity we do this for every
  583. // index, even though constant indices will not end up being
  584. // clamped.
  585. for (uint32_t index = 1; index < inst.NumInOperands(); index++) {
  586. auto compare_and_select_ids =
  587. clamping_info.add_compare_and_select_ids();
  588. compare_and_select_ids->set_first(
  589. GetFuzzerContext()->GetFreshId());
  590. compare_and_select_ids->set_second(
  591. GetFuzzerContext()->GetFreshId());
  592. // Get the bound for the component being indexed into.
  593. uint32_t bound =
  594. TransformationAddFunction::GetBoundForCompositeIndex(
  595. donor_ir_context, *should_be_composite_type);
  596. const uint32_t index_id = inst.GetSingleWordInOperand(index);
  597. auto index_inst =
  598. donor_ir_context->get_def_use_mgr()->GetDef(index_id);
  599. auto index_type_inst =
  600. donor_ir_context->get_def_use_mgr()->GetDef(
  601. index_inst->type_id());
  602. assert(index_type_inst->opcode() == SpvOpTypeInt);
  603. assert(index_type_inst->GetSingleWordInOperand(0) == 32);
  604. opt::analysis::Integer* index_int_type =
  605. donor_ir_context->get_type_mgr()
  606. ->GetType(index_type_inst->result_id())
  607. ->AsInteger();
  608. if (index_inst->opcode() != SpvOpConstant) {
  609. // We will have to clamp this index, so we need a constant
  610. // whose value is one less than the bound, to compare
  611. // against and to use as the clamped value.
  612. FindOrCreate32BitIntegerConstant(bound - 1,
  613. index_int_type->IsSigned());
  614. }
  615. should_be_composite_type =
  616. TransformationAddFunction::FollowCompositeIndex(
  617. donor_ir_context, *should_be_composite_type, index_id);
  618. }
  619. access_chain_clamping_info.push_back(clamping_info);
  620. break;
  621. }
  622. default:
  623. break;
  624. }
  625. }
  626. }
  627. // If the function contains OpKill or OpUnreachable instructions, and has
  628. // non-void return type, then we need a value %v to use in order to turn
  629. // these into instructions of the form OpReturn %v.
  630. uint32_t kill_unreachable_return_value_id;
  631. auto function_return_type_inst =
  632. donor_ir_context->get_def_use_mgr()->GetDef(
  633. function_to_donate->type_id());
  634. if (function_return_type_inst->opcode() == SpvOpTypeVoid) {
  635. // The return type is void, so we don't need a return value.
  636. kill_unreachable_return_value_id = 0;
  637. } else {
  638. // We do need a return value; we use zero.
  639. assert(function_return_type_inst->opcode() != SpvOpTypePointer &&
  640. "Function return type must not be a pointer.");
  641. kill_unreachable_return_value_id =
  642. FindOrCreateZeroConstant(original_id_to_donated_id->at(
  643. function_return_type_inst->result_id()));
  644. }
  645. // Add the function in a livesafe manner.
  646. ApplyTransformation(TransformationAddFunction(
  647. donated_instructions, loop_limiter_variable_id, loop_limit,
  648. loop_limiters, kill_unreachable_return_value_id,
  649. access_chain_clamping_info));
  650. } else {
  651. // Add the function in a non-livesafe manner.
  652. ApplyTransformation(TransformationAddFunction(donated_instructions));
  653. }
  654. }
  655. }
  656. std::vector<uint32_t>
  657. FuzzerPassDonateModules::GetFunctionsInCallGraphTopologicalOrder(
  658. opt::IRContext* context) {
  659. CallGraph call_graph(context);
  660. // This is an implementation of Kahn’s algorithm for topological sorting.
  661. // This is the sorted order of function ids that we will eventually return.
  662. std::vector<uint32_t> result;
  663. // Get a copy of the initial in-degrees of all functions. The algorithm
  664. // involves decrementing these values, hence why we work on a copy.
  665. std::map<uint32_t, uint32_t> function_in_degree =
  666. call_graph.GetFunctionInDegree();
  667. // Populate a queue with all those function ids with in-degree zero.
  668. std::queue<uint32_t> queue;
  669. for (auto& entry : function_in_degree) {
  670. if (entry.second == 0) {
  671. queue.push(entry.first);
  672. }
  673. }
  674. // Pop ids from the queue, adding them to the sorted order and decreasing the
  675. // in-degrees of their successors. A successor who's in-degree becomes zero
  676. // gets added to the queue.
  677. while (!queue.empty()) {
  678. auto next = queue.front();
  679. queue.pop();
  680. result.push_back(next);
  681. for (auto successor : call_graph.GetDirectCallees(next)) {
  682. assert(function_in_degree.at(successor) > 0 &&
  683. "The in-degree cannot be zero if the function is a successor.");
  684. function_in_degree[successor] = function_in_degree.at(successor) - 1;
  685. if (function_in_degree.at(successor) == 0) {
  686. queue.push(successor);
  687. }
  688. }
  689. }
  690. assert(result.size() == function_in_degree.size() &&
  691. "Every function should appear in the sort.");
  692. return result;
  693. }
  694. } // namespace fuzz
  695. } // namespace spvtools