/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
- /*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package dexfuzz.program;
- import dexfuzz.Log;
- import dexfuzz.rawdex.Instruction;
- import dexfuzz.rawdex.Opcode;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.LinkedList;
- import java.util.List;
- /**
- * A class that represents a CodeItem in a way that is more amenable to mutation.
- */
- public class MutatableCode {
- /**
- * To ensure we update the correct CodeItem in the raw DEX file.
- */
- public int codeItemIdx;
- /**
- * This is an index into the Program's list of MutatableCodes.
- */
- public int mutatableCodeIdx;
- /**
- * Number of registers this code uses.
- */
- public short registersSize;
- /**
- * Number of ins this code has.
- */
- public short insSize;
- /**
- * Number of outs this code has.
- */
- public short outsSize;
- /**
- * Number of tries this code has.
- */
- public short triesSize;
- /**
- * CodeTranslator is responsible for creating this, and
- * converting it back to a list of Instructions.
- */
- private List<MInsn> mutatableInsns;
- /**
- * CodeTranslator is responsible for creating this, and
- * converting it back to the correct form for CodeItems.
- */
- public List<MTryBlock> mutatableTries;
- /**
- * The name of the method this code represents.
- */
- public String name;
- public String shorty;
- public boolean isStatic;
- /**
- * The Program that owns this MutatableCode.
- * Currently used to get the size of constant pools for
- * PoolIndexChanger/RandomInstructionGenerator
- */
- public Program program;
- private short originalInVReg;
- private short tempVRegsAllocated;
- private short initialTempVReg;
- private boolean vregsNeedCopying;
- private int numMoveInsnsGenerated;
- public MutatableCode(Program program) {
- this.program = program;
- this.mutatableInsns = new LinkedList<MInsn>();
- }
- /**
- * Call this to update all instructions after the provided mInsn, to have their
- * locations adjusted by the provided offset. It will also mark that they have been updated.
- */
- public void updateInstructionLocationsAfter(MInsn mInsn, int offset) {
- boolean updating = false;
- for (MInsn mInsnChecking : mutatableInsns) {
- if (updating) {
- mInsnChecking.locationUpdated = true;
- mInsnChecking.location += offset;
- } else {
- if (mInsnChecking == mInsn) {
- updating = true;
- }
- }
- }
- }
- private void recalculateLocations() {
- int loc = 0;
- for (MInsn mInsn : mutatableInsns) {
- mInsn.location = loc;
- loc += mInsn.insn.getSize();
- }
- }
- public List<MInsn> getInstructions() {
- return Collections.unmodifiableList(mutatableInsns);
- }
- public int getInstructionCount() {
- return mutatableInsns.size();
- }
- public int getInstructionIndex(MInsn mInsn) {
- return mutatableInsns.indexOf(mInsn);
- }
- public MInsn getInstructionAt(int idx) {
- return mutatableInsns.get(idx);
- }
- public void addInstructionToEnd(MInsn mInsn) {
- mutatableInsns.add(mInsn);
- }
- public void insertInstructionAfter(MInsn toBeInserted, int insertionIdx) {
- if ((insertionIdx + 1) < mutatableInsns.size()) {
- insertInstructionAt(toBeInserted, insertionIdx + 1);
- } else {
- // Appending to end.
- MInsn finalInsn = mutatableInsns.get(mutatableInsns.size() - 1);
- toBeInserted.location = finalInsn.location + finalInsn.insn.getSize();
- mutatableInsns.add(toBeInserted);
- }
- }
- /**
- * Has same semantics as List.add()
- */
- public void insertInstructionAt(MInsn toBeInserted, int insertionIdx) {
- MInsn currentInsn = mutatableInsns.get(insertionIdx);
- toBeInserted.location = currentInsn.location;
- mutatableInsns.add(insertionIdx , toBeInserted);
- updateInstructionLocationsAfter(toBeInserted, toBeInserted.insn.getSize());
- }
- /**
- * Checks if any MTryBlock's instruction refs pointed at the 'before' MInsn,
- * and points them to the 'after' MInsn, if so. 'twoWay' will check if 'after'
- * was pointed to, and point refs to the 'before' insn.
- * (one-way is used when deleting instructions,
- * two-way is used when swapping instructions.)
- */
- private void updateTryBlocksWithReplacementInsn(MInsn before, MInsn after,
- boolean twoWay) {
- if (triesSize > 0) {
- for (MTryBlock mTryBlock : mutatableTries) {
- if (mTryBlock.startInsn == before) {
- Log.debug("Try block's first instruction was updated");
- mTryBlock.startInsn = after;
- } else if (twoWay && mTryBlock.startInsn == after) {
- Log.debug("Try block's first instruction was updated");
- mTryBlock.startInsn = before;
- }
- if (mTryBlock.endInsn == before) {
- Log.debug("Try block's last instruction was updated");
- mTryBlock.endInsn = after;
- } else if (twoWay && mTryBlock.endInsn == after) {
- Log.debug("Try block's last instruction was updated");
- mTryBlock.endInsn = before;
- }
- if (mTryBlock.catchAllHandler == before) {
- Log.debug("Try block's catch-all instruction was updated");
- mTryBlock.catchAllHandler = after;
- } else if (twoWay && mTryBlock.catchAllHandler == after) {
- Log.debug("Try block's catch-all instruction was updated");
- mTryBlock.catchAllHandler = before;
- }
- List<Integer> matchesIndicesToChange = new ArrayList<Integer>();
- List<Integer> replacementIndicesToChange = null;
- if (twoWay) {
- replacementIndicesToChange = new ArrayList<Integer>();
- }
- int idx = 0;
- for (MInsn handler : mTryBlock.handlers) {
- if (handler == before) {
- matchesIndicesToChange.add(idx);
- Log.debug("Try block's handler instruction was updated");
- } else if (twoWay && handler == after) {
- replacementIndicesToChange.add(idx);
- Log.debug("Try block's handler instruction was updated");
- }
- idx++;
- }
- for (int idxToChange : matchesIndicesToChange) {
- mTryBlock.handlers.set(idxToChange, after);
- }
- if (twoWay) {
- for (int idxToChange : replacementIndicesToChange) {
- mTryBlock.handlers.set(idxToChange, before);
- }
- }
- }
- }
- }
- /**
- * The actual implementation of deleteInstruction called by
- * the single-argument deleteInstructions.
- */
- private void deleteInstructionFull(MInsn toBeDeleted, int toBeDeletedIdx) {
- // Make sure we update all locations afterwards first.
- updateInstructionLocationsAfter(toBeDeleted, -(toBeDeleted.insn.getSize()));
- // Remove it.
- mutatableInsns.remove(toBeDeletedIdx);
- // Update any branch instructions that branched to the instruction we just deleted!
- // First, pick the replacement target.
- int replacementTargetIdx = toBeDeletedIdx;
- if (replacementTargetIdx == mutatableInsns.size()) {
- replacementTargetIdx--;
- }
- MInsn replacementTarget = mutatableInsns.get(replacementTargetIdx);
- for (MInsn mInsn : mutatableInsns) {
- if (mInsn instanceof MBranchInsn) {
- // Check if this branch insn points at the insn we just deleted.
- MBranchInsn branchInsn = (MBranchInsn) mInsn;
- MInsn target = branchInsn.target;
- if (target == toBeDeleted) {
- Log.debug(branchInsn + " was pointing at the deleted instruction, updated.");
- branchInsn.target = replacementTarget;
- }
- } else if (mInsn instanceof MSwitchInsn) {
- // Check if any of this switch insn's targets points at the insn we just deleted.
- MSwitchInsn switchInsn = (MSwitchInsn) mInsn;
- List<Integer> indicesToChange = new ArrayList<Integer>();
- int idx = 0;
- for (MInsn target : switchInsn.targets) {
- if (target == toBeDeleted) {
- indicesToChange.add(idx);
- Log.debug(switchInsn + "[" + idx
- + "] was pointing at the deleted instruction, updated.");
- }
- idx++;
- }
- for (int idxToChange : indicesToChange) {
- switchInsn.targets.remove(idxToChange);
- switchInsn.targets.add(idxToChange, replacementTarget);
- }
- }
- }
- // Now update the try blocks.
- updateTryBlocksWithReplacementInsn(toBeDeleted, replacementTarget, false);
- }
- /**
- * Delete the provided MInsn.
- */
- public void deleteInstruction(MInsn toBeDeleted) {
- deleteInstructionFull(toBeDeleted, mutatableInsns.indexOf(toBeDeleted));
- }
- /**
- * Delete the MInsn at the provided index.
- */
- public void deleteInstruction(int toBeDeletedIdx) {
- deleteInstructionFull(mutatableInsns.get(toBeDeletedIdx), toBeDeletedIdx);
- }
- public void swapInstructionsByIndex(int aIdx, int bIdx) {
- MInsn aInsn = mutatableInsns.get(aIdx);
- MInsn bInsn = mutatableInsns.get(bIdx);
- mutatableInsns.set(aIdx, bInsn);
- mutatableInsns.set(bIdx, aInsn);
- updateTryBlocksWithReplacementInsn(aInsn, bInsn, true);
- recalculateLocations();
- }
- /**
- * Some mutators may require the use of temporary registers. For instance,
- * to easily add in printing of values without having to look for registers
- * that aren't currently live.
- * The idea is to allocate these registers at the top of the set of registers.
- * Because this will then shift where the arguments to the method are, we then
- * change the start of the method to copy the arguments to the method
- * into the place where the rest of the method's code expects them to be.
- * Call allocateTemporaryVRegs(n), then use getTemporaryVReg(n),
- * and then make sure finishedUsingTemporaryVRegs() is called!
- */
- public void allocateTemporaryVRegs(int count) {
- if (count > tempVRegsAllocated) {
- if (tempVRegsAllocated == 0) {
- Log.info("Allocating temporary vregs for method...");
- initialTempVReg = registersSize;
- originalInVReg = (short) (registersSize - insSize);
- } else {
- Log.info("Extending allocation of temporary vregs for method...");
- }
- registersSize = (short) (initialTempVReg + count);
- if (outsSize < count) {
- outsSize = (short) count;
- }
- vregsNeedCopying = true;
- tempVRegsAllocated = (short) count;
- }
- }
- public int getTemporaryVReg(int number) {
- if (number >= tempVRegsAllocated) {
- Log.errorAndQuit("Not allocated enough temporary vregs!");
- }
- return initialTempVReg + number;
- }
- public void finishedUsingTemporaryVRegs() {
- if (tempVRegsAllocated > 0 && vregsNeedCopying) {
- // Just delete all the move instructions and generate again, if we already have some.
- while (numMoveInsnsGenerated > 0) {
- deleteInstruction(0);
- numMoveInsnsGenerated--;
- }
- Log.info("Moving 'in' vregs to correct locations after allocating temporary vregs");
- int shortyIdx = 0;
- if (isStatic) {
- shortyIdx = 1;
- }
- int insertionCounter = 0;
- // Insert copy insns that move all the in VRs down.
- for (int i = 0; i < insSize; i++) {
- MInsn moveInsn = new MInsn();
- moveInsn.insn = new Instruction();
- moveInsn.insn.vregA = originalInVReg + i;
- moveInsn.insn.vregB = originalInVReg + i + tempVRegsAllocated;
- char type = 'L';
- if (shortyIdx > 0) {
- type = shorty.charAt(shortyIdx);
- }
- shortyIdx++;
- if (type == 'L') {
- moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_OBJECT_16);
- } else if (type == 'D' || type == 'J') {
- moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_WIDE_16);
- i++;
- } else {
- moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_16);
- }
- insertInstructionAt(moveInsn, insertionCounter);
- insertionCounter++;
- Log.info("Temp vregs creation, Added instruction " + moveInsn);
- numMoveInsnsGenerated++;
- }
- vregsNeedCopying = false;
- }
- }
- /**
- * When we insert new Field/Type/MethodIds into the DEX file, this may shunt some Ids
- * into a new position in the table. If this happens, every reference to the Ids must
- * be updated! Because CodeItems have their Instructions wrapped into a graph of MInsns
- * during mutation, they don't have a view of all their instructions during mutation,
- * and so if they are asked to update their instructions' indices into the tables, they
- * must call this method to get the actual list of instructions they currently own.
- */
- public List<Instruction> requestLatestInstructions() {
- List<Instruction> latestInsns = new ArrayList<Instruction>();
- for (MInsn mInsn : mutatableInsns) {
- latestInsns.add(mInsn.insn);
- }
- return latestInsns;
- }
- }