PageRenderTime 63ms CodeModel.GetById 36ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/dexfuzz/src/dexfuzz/program/MutatableCode.java

https://gitlab.com/Atomic-ROM/art
Java | 410 lines | 251 code | 54 blank | 105 comment | 72 complexity | dd6a53e9aad1962ae79cf8be7b0a36db MD5 | raw file
  1. /*
  2. * Copyright (C) 2014 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package dexfuzz.program;
  17. import dexfuzz.Log;
  18. import dexfuzz.rawdex.Instruction;
  19. import dexfuzz.rawdex.Opcode;
  20. import java.util.ArrayList;
  21. import java.util.Collections;
  22. import java.util.LinkedList;
  23. import java.util.List;
  24. /**
  25. * A class that represents a CodeItem in a way that is more amenable to mutation.
  26. */
  27. public class MutatableCode {
  28. /**
  29. * To ensure we update the correct CodeItem in the raw DEX file.
  30. */
  31. public int codeItemIdx;
  32. /**
  33. * This is an index into the Program's list of MutatableCodes.
  34. */
  35. public int mutatableCodeIdx;
  36. /**
  37. * Number of registers this code uses.
  38. */
  39. public short registersSize;
  40. /**
  41. * Number of ins this code has.
  42. */
  43. public short insSize;
  44. /**
  45. * Number of outs this code has.
  46. */
  47. public short outsSize;
  48. /**
  49. * Number of tries this code has.
  50. */
  51. public short triesSize;
  52. /**
  53. * CodeTranslator is responsible for creating this, and
  54. * converting it back to a list of Instructions.
  55. */
  56. private List<MInsn> mutatableInsns;
  57. /**
  58. * CodeTranslator is responsible for creating this, and
  59. * converting it back to the correct form for CodeItems.
  60. */
  61. public List<MTryBlock> mutatableTries;
  62. /**
  63. * The name of the method this code represents.
  64. */
  65. public String name;
  66. public String shorty;
  67. public boolean isStatic;
  68. /**
  69. * The Program that owns this MutatableCode.
  70. * Currently used to get the size of constant pools for
  71. * PoolIndexChanger/RandomInstructionGenerator
  72. */
  73. public Program program;
  74. private short originalInVReg;
  75. private short tempVRegsAllocated;
  76. private short initialTempVReg;
  77. private boolean vregsNeedCopying;
  78. private int numMoveInsnsGenerated;
  79. public MutatableCode(Program program) {
  80. this.program = program;
  81. this.mutatableInsns = new LinkedList<MInsn>();
  82. }
  83. /**
  84. * Call this to update all instructions after the provided mInsn, to have their
  85. * locations adjusted by the provided offset. It will also mark that they have been updated.
  86. */
  87. public void updateInstructionLocationsAfter(MInsn mInsn, int offset) {
  88. boolean updating = false;
  89. for (MInsn mInsnChecking : mutatableInsns) {
  90. if (updating) {
  91. mInsnChecking.locationUpdated = true;
  92. mInsnChecking.location += offset;
  93. } else {
  94. if (mInsnChecking == mInsn) {
  95. updating = true;
  96. }
  97. }
  98. }
  99. }
  100. private void recalculateLocations() {
  101. int loc = 0;
  102. for (MInsn mInsn : mutatableInsns) {
  103. mInsn.location = loc;
  104. loc += mInsn.insn.getSize();
  105. }
  106. }
  107. public List<MInsn> getInstructions() {
  108. return Collections.unmodifiableList(mutatableInsns);
  109. }
  110. public int getInstructionCount() {
  111. return mutatableInsns.size();
  112. }
  113. public int getInstructionIndex(MInsn mInsn) {
  114. return mutatableInsns.indexOf(mInsn);
  115. }
  116. public MInsn getInstructionAt(int idx) {
  117. return mutatableInsns.get(idx);
  118. }
  119. public void addInstructionToEnd(MInsn mInsn) {
  120. mutatableInsns.add(mInsn);
  121. }
  122. public void insertInstructionAfter(MInsn toBeInserted, int insertionIdx) {
  123. if ((insertionIdx + 1) < mutatableInsns.size()) {
  124. insertInstructionAt(toBeInserted, insertionIdx + 1);
  125. } else {
  126. // Appending to end.
  127. MInsn finalInsn = mutatableInsns.get(mutatableInsns.size() - 1);
  128. toBeInserted.location = finalInsn.location + finalInsn.insn.getSize();
  129. mutatableInsns.add(toBeInserted);
  130. }
  131. }
  132. /**
  133. * Has same semantics as List.add()
  134. */
  135. public void insertInstructionAt(MInsn toBeInserted, int insertionIdx) {
  136. MInsn currentInsn = mutatableInsns.get(insertionIdx);
  137. toBeInserted.location = currentInsn.location;
  138. mutatableInsns.add(insertionIdx , toBeInserted);
  139. updateInstructionLocationsAfter(toBeInserted, toBeInserted.insn.getSize());
  140. }
  141. /**
  142. * Checks if any MTryBlock's instruction refs pointed at the 'before' MInsn,
  143. * and points them to the 'after' MInsn, if so. 'twoWay' will check if 'after'
  144. * was pointed to, and point refs to the 'before' insn.
  145. * (one-way is used when deleting instructions,
  146. * two-way is used when swapping instructions.)
  147. */
  148. private void updateTryBlocksWithReplacementInsn(MInsn before, MInsn after,
  149. boolean twoWay) {
  150. if (triesSize > 0) {
  151. for (MTryBlock mTryBlock : mutatableTries) {
  152. if (mTryBlock.startInsn == before) {
  153. Log.debug("Try block's first instruction was updated");
  154. mTryBlock.startInsn = after;
  155. } else if (twoWay && mTryBlock.startInsn == after) {
  156. Log.debug("Try block's first instruction was updated");
  157. mTryBlock.startInsn = before;
  158. }
  159. if (mTryBlock.endInsn == before) {
  160. Log.debug("Try block's last instruction was updated");
  161. mTryBlock.endInsn = after;
  162. } else if (twoWay && mTryBlock.endInsn == after) {
  163. Log.debug("Try block's last instruction was updated");
  164. mTryBlock.endInsn = before;
  165. }
  166. if (mTryBlock.catchAllHandler == before) {
  167. Log.debug("Try block's catch-all instruction was updated");
  168. mTryBlock.catchAllHandler = after;
  169. } else if (twoWay && mTryBlock.catchAllHandler == after) {
  170. Log.debug("Try block's catch-all instruction was updated");
  171. mTryBlock.catchAllHandler = before;
  172. }
  173. List<Integer> matchesIndicesToChange = new ArrayList<Integer>();
  174. List<Integer> replacementIndicesToChange = null;
  175. if (twoWay) {
  176. replacementIndicesToChange = new ArrayList<Integer>();
  177. }
  178. int idx = 0;
  179. for (MInsn handler : mTryBlock.handlers) {
  180. if (handler == before) {
  181. matchesIndicesToChange.add(idx);
  182. Log.debug("Try block's handler instruction was updated");
  183. } else if (twoWay && handler == after) {
  184. replacementIndicesToChange.add(idx);
  185. Log.debug("Try block's handler instruction was updated");
  186. }
  187. idx++;
  188. }
  189. for (int idxToChange : matchesIndicesToChange) {
  190. mTryBlock.handlers.set(idxToChange, after);
  191. }
  192. if (twoWay) {
  193. for (int idxToChange : replacementIndicesToChange) {
  194. mTryBlock.handlers.set(idxToChange, before);
  195. }
  196. }
  197. }
  198. }
  199. }
  200. /**
  201. * The actual implementation of deleteInstruction called by
  202. * the single-argument deleteInstructions.
  203. */
  204. private void deleteInstructionFull(MInsn toBeDeleted, int toBeDeletedIdx) {
  205. // Make sure we update all locations afterwards first.
  206. updateInstructionLocationsAfter(toBeDeleted, -(toBeDeleted.insn.getSize()));
  207. // Remove it.
  208. mutatableInsns.remove(toBeDeletedIdx);
  209. // Update any branch instructions that branched to the instruction we just deleted!
  210. // First, pick the replacement target.
  211. int replacementTargetIdx = toBeDeletedIdx;
  212. if (replacementTargetIdx == mutatableInsns.size()) {
  213. replacementTargetIdx--;
  214. }
  215. MInsn replacementTarget = mutatableInsns.get(replacementTargetIdx);
  216. for (MInsn mInsn : mutatableInsns) {
  217. if (mInsn instanceof MBranchInsn) {
  218. // Check if this branch insn points at the insn we just deleted.
  219. MBranchInsn branchInsn = (MBranchInsn) mInsn;
  220. MInsn target = branchInsn.target;
  221. if (target == toBeDeleted) {
  222. Log.debug(branchInsn + " was pointing at the deleted instruction, updated.");
  223. branchInsn.target = replacementTarget;
  224. }
  225. } else if (mInsn instanceof MSwitchInsn) {
  226. // Check if any of this switch insn's targets points at the insn we just deleted.
  227. MSwitchInsn switchInsn = (MSwitchInsn) mInsn;
  228. List<Integer> indicesToChange = new ArrayList<Integer>();
  229. int idx = 0;
  230. for (MInsn target : switchInsn.targets) {
  231. if (target == toBeDeleted) {
  232. indicesToChange.add(idx);
  233. Log.debug(switchInsn + "[" + idx
  234. + "] was pointing at the deleted instruction, updated.");
  235. }
  236. idx++;
  237. }
  238. for (int idxToChange : indicesToChange) {
  239. switchInsn.targets.remove(idxToChange);
  240. switchInsn.targets.add(idxToChange, replacementTarget);
  241. }
  242. }
  243. }
  244. // Now update the try blocks.
  245. updateTryBlocksWithReplacementInsn(toBeDeleted, replacementTarget, false);
  246. }
  247. /**
  248. * Delete the provided MInsn.
  249. */
  250. public void deleteInstruction(MInsn toBeDeleted) {
  251. deleteInstructionFull(toBeDeleted, mutatableInsns.indexOf(toBeDeleted));
  252. }
  253. /**
  254. * Delete the MInsn at the provided index.
  255. */
  256. public void deleteInstruction(int toBeDeletedIdx) {
  257. deleteInstructionFull(mutatableInsns.get(toBeDeletedIdx), toBeDeletedIdx);
  258. }
  259. public void swapInstructionsByIndex(int aIdx, int bIdx) {
  260. MInsn aInsn = mutatableInsns.get(aIdx);
  261. MInsn bInsn = mutatableInsns.get(bIdx);
  262. mutatableInsns.set(aIdx, bInsn);
  263. mutatableInsns.set(bIdx, aInsn);
  264. updateTryBlocksWithReplacementInsn(aInsn, bInsn, true);
  265. recalculateLocations();
  266. }
  267. /**
  268. * Some mutators may require the use of temporary registers. For instance,
  269. * to easily add in printing of values without having to look for registers
  270. * that aren't currently live.
  271. * The idea is to allocate these registers at the top of the set of registers.
  272. * Because this will then shift where the arguments to the method are, we then
  273. * change the start of the method to copy the arguments to the method
  274. * into the place where the rest of the method's code expects them to be.
  275. * Call allocateTemporaryVRegs(n), then use getTemporaryVReg(n),
  276. * and then make sure finishedUsingTemporaryVRegs() is called!
  277. */
  278. public void allocateTemporaryVRegs(int count) {
  279. if (count > tempVRegsAllocated) {
  280. if (tempVRegsAllocated == 0) {
  281. Log.info("Allocating temporary vregs for method...");
  282. initialTempVReg = registersSize;
  283. originalInVReg = (short) (registersSize - insSize);
  284. } else {
  285. Log.info("Extending allocation of temporary vregs for method...");
  286. }
  287. registersSize = (short) (initialTempVReg + count);
  288. if (outsSize < count) {
  289. outsSize = (short) count;
  290. }
  291. vregsNeedCopying = true;
  292. tempVRegsAllocated = (short) count;
  293. }
  294. }
  295. public int getTemporaryVReg(int number) {
  296. if (number >= tempVRegsAllocated) {
  297. Log.errorAndQuit("Not allocated enough temporary vregs!");
  298. }
  299. return initialTempVReg + number;
  300. }
  301. public void finishedUsingTemporaryVRegs() {
  302. if (tempVRegsAllocated > 0 && vregsNeedCopying) {
  303. // Just delete all the move instructions and generate again, if we already have some.
  304. while (numMoveInsnsGenerated > 0) {
  305. deleteInstruction(0);
  306. numMoveInsnsGenerated--;
  307. }
  308. Log.info("Moving 'in' vregs to correct locations after allocating temporary vregs");
  309. int shortyIdx = 0;
  310. if (isStatic) {
  311. shortyIdx = 1;
  312. }
  313. int insertionCounter = 0;
  314. // Insert copy insns that move all the in VRs down.
  315. for (int i = 0; i < insSize; i++) {
  316. MInsn moveInsn = new MInsn();
  317. moveInsn.insn = new Instruction();
  318. moveInsn.insn.vregA = originalInVReg + i;
  319. moveInsn.insn.vregB = originalInVReg + i + tempVRegsAllocated;
  320. char type = 'L';
  321. if (shortyIdx > 0) {
  322. type = shorty.charAt(shortyIdx);
  323. }
  324. shortyIdx++;
  325. if (type == 'L') {
  326. moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_OBJECT_16);
  327. } else if (type == 'D' || type == 'J') {
  328. moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_WIDE_16);
  329. i++;
  330. } else {
  331. moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_16);
  332. }
  333. insertInstructionAt(moveInsn, insertionCounter);
  334. insertionCounter++;
  335. Log.info("Temp vregs creation, Added instruction " + moveInsn);
  336. numMoveInsnsGenerated++;
  337. }
  338. vregsNeedCopying = false;
  339. }
  340. }
  341. /**
  342. * When we insert new Field/Type/MethodIds into the DEX file, this may shunt some Ids
  343. * into a new position in the table. If this happens, every reference to the Ids must
  344. * be updated! Because CodeItems have their Instructions wrapped into a graph of MInsns
  345. * during mutation, they don't have a view of all their instructions during mutation,
  346. * and so if they are asked to update their instructions' indices into the tables, they
  347. * must call this method to get the actual list of instructions they currently own.
  348. */
  349. public List<Instruction> requestLatestInstructions() {
  350. List<Instruction> latestInsns = new ArrayList<Instruction>();
  351. for (MInsn mInsn : mutatableInsns) {
  352. latestInsns.add(mInsn.insn);
  353. }
  354. return latestInsns;
  355. }
  356. }