From 4eb5e485782bc1c6ae28e608ba4d618e1a1da994 Mon Sep 17 00:00:00 2001 From: Ben Gruver Date: Mon, 15 Jul 2019 15:34:13 -0700 Subject: [PATCH] Improve how invalid references are handled This adds a validateReference() method to the Reference interface, where subclasses can check themselves and throw an InvalidReferenceException --- .../jf/baksmali/Adaptors/ClassDefinition.java | 23 +- .../Format/InstructionMethodItem.java | 51 ++-- .../baksmali/Adaptors/MethodDefinition.java | 28 +- .../baksmali/InstructionMethodItemTest.java | 276 ++++++++++++++++++ .../java/org/jf/dexlib2/ReferenceType.java | 21 -- .../base/reference/BaseCallSiteReference.java | 2 +- .../base/reference/BaseFieldReference.java | 2 +- .../reference/BaseMethodHandleReference.java | 2 +- .../reference/BaseMethodProtoReference.java | 2 +- .../base/reference/BaseMethodReference.java | 2 +- .../dexlib2/base/reference/BaseReference.java | 41 +++ .../base/reference/BaseStringReference.java | 2 +- .../base/reference/BaseTypeReference.java | 2 +- .../instruction/DexBackedInstruction20bc.java | 15 +- .../reference/DexBackedCallSiteReference.java | 7 + .../reference/DexBackedFieldReference.java | 18 +- .../DexBackedMethodHandleReference.java | 13 + .../DexBackedMethodProtoReference.java | 19 +- .../reference/DexBackedMethodReference.java | 19 +- .../reference/DexBackedStringReference.java | 7 + .../reference/DexBackedTypeReference.java | 7 + .../jf/dexlib2/iface/reference/Reference.java | 45 ++- 22 files changed, 502 insertions(+), 102 deletions(-) create mode 100644 baksmali/src/test/java/org/jf/baksmali/InstructionMethodItemTest.java create mode 100644 dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseReference.java diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java index 361826da..73603f19 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/ClassDefinition.java @@ -31,18 +31,21 @@ package org.jf.baksmali.Adaptors; import org.jf.baksmali.BaksmaliOptions; import org.jf.dexlib2.AccessFlags; import org.jf.dexlib2.dexbacked.DexBackedClassDef; -import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex; import org.jf.dexlib2.iface.*; import org.jf.dexlib2.iface.instruction.Instruction; import org.jf.dexlib2.iface.instruction.formats.Instruction21c; import org.jf.dexlib2.iface.reference.FieldReference; +import org.jf.dexlib2.iface.reference.Reference; import org.jf.dexlib2.util.ReferenceUtil; import org.jf.util.IndentingWriter; import org.jf.util.StringUtils; import javax.annotation.Nonnull; import java.io.IOException; -import java.util.*; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class ClassDefinition { @Nonnull public final BaksmaliOptions options; @@ -79,16 +82,14 @@ public class ClassDefinition { case SPUT_SHORT: case SPUT_WIDE: { Instruction21c ins = (Instruction21c)instruction; - FieldReference fieldRef = null; + FieldReference fieldRef = (FieldReference)ins.getReference(); try { - fieldRef = (FieldReference)ins.getReference(); - } catch (InvalidItemIndex ex) { - // just ignore it for now. We'll deal with it later, when processing the instructions - // themselves - } - if (fieldRef != null && - fieldRef.getDefiningClass().equals((classDef.getType()))) { - fieldsSetInStaticConstructor.add(ReferenceUtil.getShortFieldDescriptor(fieldRef)); + fieldRef.validateReference(); + if (fieldRef.getDefiningClass().equals((classDef.getType()))) { + fieldsSetInStaticConstructor.add(ReferenceUtil.getShortFieldDescriptor(fieldRef)); + } + } catch (Reference.InvalidReferenceException ex) { + // Just ignore for now. We'll deal with it when processing the instruction } break; } diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java index d5b0bf34..240c99d8 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java @@ -32,12 +32,10 @@ import org.jf.baksmali.Adaptors.MethodDefinition; import org.jf.baksmali.Adaptors.MethodDefinition.InvalidSwitchPayload; import org.jf.baksmali.Adaptors.MethodItem; import org.jf.baksmali.Adaptors.ReferenceFormatter; -import org.jf.baksmali.Renderers.LongRenderer; import org.jf.baksmali.BaksmaliOptions; +import org.jf.baksmali.Renderers.LongRenderer; import org.jf.dexlib2.Opcode; -import org.jf.dexlib2.ReferenceType; import org.jf.dexlib2.VerificationError; -import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex; import org.jf.dexlib2.iface.instruction.*; import org.jf.dexlib2.iface.instruction.formats.Instruction20bc; import org.jf.dexlib2.iface.instruction.formats.Instruction31t; @@ -81,14 +79,6 @@ public class InstructionMethodItem extends MethodItem { return opcode.isVolatileFieldAccessor() || opcode == Opcode.THROW_VERIFICATION_ERROR; } - private String writeInvalidItemIndex(InvalidItemIndex ex, int type, IndentingWriter writer) - throws IOException { - writer.write("#"); - writer.write(ex.getMessage()); - writer.write("\n"); - return String.format("%s@%d", ReferenceType.toString(type), ex.getInvalidIndex()); - } - private interface Writable { void writeTo(IndentingWriter writer) throws IOException; } @@ -122,8 +112,11 @@ public class InstructionMethodItem extends MethodItem { classContext = null; } + Reference reference = referenceInstruction.getReference(); + try { - Reference reference = referenceInstruction.getReference(); + reference.validateReference(); + if (reference instanceof CallSiteReference) { referenceWritable = indentingWriter -> { ReferenceFormatter.writeCallSiteReference(indentingWriter, (CallSiteReference)reference); @@ -133,16 +126,14 @@ public class InstructionMethodItem extends MethodItem { indentingWriter.write(ReferenceUtil.getReferenceString(reference, classContext)); }; } - } catch (InvalidItemIndex ex) { + } catch (Reference.InvalidReferenceException ex) { commentOutInstruction = true; - String referenceString = writeInvalidItemIndex(ex, referenceInstruction.getReferenceType(), - writer); - referenceWritable = indentingWriter -> writer.write(referenceString); - } catch (ReferenceType.InvalidReferenceTypeException ex) { - writer.write("#invalid reference type: "); - writer.printSignedIntAsDec(ex.getReferenceType()); - commentOutInstruction = true; - referenceWritable = indentingWriter -> writer.write("invalid_reference"); + writer.write("#"); + writer.write(ex.getMessage()); + writer.write("\n"); + referenceWritable = indentingWriter -> { + indentingWriter.write(ex.getInvalidReferenceRepresentation()); + }; } if (instruction instanceof DualReferenceInstruction) { @@ -150,19 +141,19 @@ public class InstructionMethodItem extends MethodItem { (DualReferenceInstruction) instruction; try { Reference reference2 = dualReferenceInstruction.getReference2(); + reference2.validateReference(); + referenceWritable2 = indentingWriter -> { indentingWriter.write(ReferenceUtil.getReferenceString(reference2, classContext)); }; - } catch (InvalidItemIndex ex) { + } catch (Reference.InvalidReferenceException ex) { commentOutInstruction = true; - String referenceString = writeInvalidItemIndex(ex, - dualReferenceInstruction.getReferenceType2(), writer); - referenceWritable2 = indentingWriter -> indentingWriter.write(referenceString); - } catch (ReferenceType.InvalidReferenceTypeException ex) { - writer.write("#invalid reference type: "); - writer.printSignedIntAsDec(ex.getReferenceType()); - commentOutInstruction = true; - referenceWritable2 = indentingWriter -> indentingWriter.write("invalid reference"); + writer.write("#"); + writer.write(ex.getMessage()); + writer.write("\n"); + referenceWritable = indentingWriter -> { + indentingWriter.write(ex.getInvalidReferenceRepresentation()); + }; } } } diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java index 8161fe49..7f70c54c 100644 --- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java +++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java @@ -40,7 +40,6 @@ import org.jf.dexlib2.ReferenceType; import org.jf.dexlib2.analysis.AnalysisException; import org.jf.dexlib2.analysis.AnalyzedInstruction; import org.jf.dexlib2.analysis.MethodAnalyzer; -import org.jf.dexlib2.dexbacked.DexBackedDexFile.InvalidItemIndex; import org.jf.dexlib2.iface.*; import org.jf.dexlib2.iface.debug.DebugItem; import org.jf.dexlib2.iface.instruction.Instruction; @@ -48,6 +47,7 @@ import org.jf.dexlib2.iface.instruction.OffsetInstruction; import org.jf.dexlib2.iface.instruction.ReferenceInstruction; import org.jf.dexlib2.iface.instruction.formats.Instruction31t; import org.jf.dexlib2.iface.reference.MethodReference; +import org.jf.dexlib2.iface.reference.Reference; import org.jf.dexlib2.immutable.instruction.ImmutableInstruction31t; import org.jf.dexlib2.util.InstructionOffsetMap; import org.jf.dexlib2.util.InstructionOffsetMap.InvalidInstructionOffset; @@ -437,21 +437,21 @@ public class MethodDefinition { Opcode opcode = instruction.getOpcode(); if (opcode.referenceType == ReferenceType.METHOD) { - MethodReference methodReference = null; - try { - methodReference = (MethodReference)((ReferenceInstruction)instruction).getReference(); - } catch (InvalidItemIndex ex) { - // just ignore it for now. We'll deal with it later, when processing the instructions - // themselves - } + MethodReference methodReference = + (MethodReference)((ReferenceInstruction)instruction).getReference(); - if (methodReference != null && - SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodReference.getName())) { - AccessedMember accessedMember = - classDef.options.syntheticAccessorResolver.getAccessedMember(methodReference); - if (accessedMember != null) { - methodItems.add(new SyntheticAccessCommentMethodItem(accessedMember, currentCodeAddress)); + try { + methodReference.validateReference(); + + if (SyntheticAccessorResolver.looksLikeSyntheticAccessor(methodReference.getName())) { + AccessedMember accessedMember = + classDef.options.syntheticAccessorResolver.getAccessedMember(methodReference); + if (accessedMember != null) { + methodItems.add(new SyntheticAccessCommentMethodItem(accessedMember, currentCodeAddress)); + } } + } catch (Reference.InvalidReferenceException e) { + // Just ignore for now. We'll deal with it when processing the instruction } } } diff --git a/baksmali/src/test/java/org/jf/baksmali/InstructionMethodItemTest.java b/baksmali/src/test/java/org/jf/baksmali/InstructionMethodItemTest.java new file mode 100644 index 00000000..fe7bb6e7 --- /dev/null +++ b/baksmali/src/test/java/org/jf/baksmali/InstructionMethodItemTest.java @@ -0,0 +1,276 @@ +/* + * Copyright 2019, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.baksmali; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import org.jf.baksmali.Adaptors.ClassDefinition; +import org.jf.baksmali.Adaptors.Format.InstructionMethodItem; +import org.jf.baksmali.Adaptors.MethodDefinition; +import org.jf.baksmali.Adaptors.RegisterFormatter; +import org.jf.dexlib2.Format; +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.ReferenceType; +import org.jf.dexlib2.base.reference.BaseMethodReference; +import org.jf.dexlib2.base.reference.BaseStringReference; +import org.jf.dexlib2.base.reference.BaseTypeReference; +import org.jf.dexlib2.iface.*; +import org.jf.dexlib2.iface.debug.DebugItem; +import org.jf.dexlib2.iface.instruction.Instruction; +import org.jf.dexlib2.iface.instruction.formats.Instruction21c; +import org.jf.dexlib2.iface.reference.Reference; +import org.jf.util.IndentingWriter; +import org.junit.Assert; +import org.junit.Test; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.IOException; +import java.io.StringWriter; +import java.util.List; +import java.util.Set; + +public class InstructionMethodItemTest { + + @Test + public void testInvalidReference() throws IOException { + + Instruction21c instruction = new Instruction21c() { + @Override + public int getRegisterA() { + return 0; + } + + @Nonnull + @Override + public Reference getReference() { + return new BaseStringReference() { + @Override + public void validateReference() throws InvalidReferenceException { + throw new InvalidReferenceException("blahblahblah"); + } + + @Nonnull + @Override + public String getString() { + throw new RuntimeException("invalid reference"); + } + }; + } + + @Override + public int getReferenceType() { + return ReferenceType.STRING; + } + + @Override + public Opcode getOpcode() { + return Opcode.CONST_STRING; + } + + @Override + public int getCodeUnits() { + return Format.Format21c.size / 2; + } + }; + + + MethodImplementation methodImplementation = new MethodImplementation() { + @Override + public int getRegisterCount() { + return 1; + } + + @Nonnull + @Override + public Iterable getInstructions() { + return ImmutableList.of(instruction); + } + + @Nonnull + @Override + public List> getTryBlocks() { + return ImmutableList.of(); + } + + @Nonnull + @Override + public Iterable getDebugItems() { + return ImmutableList.of(); + } + }; + + Method method = new TestMethod(methodImplementation); + + ClassDefinition classDefinition = new ClassDefinition( + new BaksmaliOptions(), new TestClassDef()); + + MethodDefinition methodDefinition = new MethodDefinition(classDefinition, method, methodImplementation); + methodDefinition.registerFormatter = new RegisterFormatter(new BaksmaliOptions(), 1, 0); + + InstructionMethodItem methodItem = new InstructionMethodItem(methodDefinition, 0, instruction); + + StringWriter stringWriter = new StringWriter(); + IndentingWriter indentingWriter = new IndentingWriter(stringWriter); + methodItem.writeTo(indentingWriter); + + Assert.assertEquals("#Invalid reference\n#const-string v0, blahblahblah\nnop", stringWriter.toString()); + } + + private static class TestMethod extends BaseMethodReference implements Method { + private final MethodImplementation methodImplementation; + + public TestMethod(MethodImplementation methodImplementation) { + this.methodImplementation = methodImplementation; + } + + @Nonnull + @Override + public List getParameters() { + return ImmutableList.of(); + } + + @Override + public int getAccessFlags() { + return 0; + } + + @Nonnull + @Override + public Set getAnnotations() { + return ImmutableSet.of(); + } + + @Nullable + @Override + public MethodImplementation getImplementation() { + return methodImplementation; + } + + @Nonnull + @Override + public String getDefiningClass() { + return "Ltest;"; + } + + @Nonnull + @Override + public String getName() { + return "test"; + } + + @Nonnull + @Override + public List getParameterTypes() { + return ImmutableList.of(); + } + + @Nonnull + @Override + public String getReturnType() { + return "V"; + } + } + + private static class TestClassDef extends BaseTypeReference implements ClassDef { + @Override + public int getAccessFlags() { + return 0; + } + + @Nullable + @Override + public String getSuperclass() { + return "Ljava/lang/Object;"; + } + + @Nonnull + @Override + public List getInterfaces() { + return ImmutableList.of(); + } + + @Nullable + @Override + public String getSourceFile() { + return null; + } + + @Nonnull + @Override + public Set getAnnotations() { + return ImmutableSet.of(); + } + + @Nonnull + @Override + public Iterable getStaticFields() { + return ImmutableList.of(); + } + + @Nonnull + @Override + public Iterable getInstanceFields() { + return ImmutableList.of(); + } + + @Nonnull + @Override + public Iterable getFields() { + return ImmutableList.of(); + } + + @Nonnull + @Override + public Iterable getDirectMethods() { + return ImmutableList.of(); + } + + @Nonnull + @Override + public Iterable getVirtualMethods() { + return ImmutableList.of(); + } + + @Nonnull + @Override + public Iterable getMethods() { + return ImmutableList.of(); + } + + @Nonnull + @Override + public String getType() { + return "Ltest;"; + } + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java b/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java index 03594759..8b774162 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/ReferenceType.java @@ -44,27 +44,6 @@ public final class ReferenceType { public static final int METHOD_HANDLE = 6; public static final int NONE = 7; - public static String toString(int referenceType) { - switch (referenceType) { - case STRING: - return "string"; - case TYPE: - return "type"; - case FIELD: - return "field"; - case METHOD: - return "method"; - case METHOD_PROTO: - return "method_proto"; - case CALL_SITE: - return "call_site"; - case METHOD_HANDLE: - return "method_handle"; - default: - throw new InvalidReferenceTypeException(referenceType); - } - } - public static int getReferenceType(Reference reference) { if (reference instanceof StringReference) { return STRING; diff --git a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseCallSiteReference.java b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseCallSiteReference.java index 700454c2..304075af 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseCallSiteReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseCallSiteReference.java @@ -33,7 +33,7 @@ package org.jf.dexlib2.base.reference; import org.jf.dexlib2.iface.reference.CallSiteReference; -public abstract class BaseCallSiteReference implements CallSiteReference { +public abstract class BaseCallSiteReference extends BaseReference implements CallSiteReference { @Override public int hashCode() { int hashCode = getName().hashCode(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseFieldReference.java b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseFieldReference.java index 862e342b..d303082b 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseFieldReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseFieldReference.java @@ -37,7 +37,7 @@ import org.jf.dexlib2.util.ReferenceUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public abstract class BaseFieldReference implements FieldReference { +public abstract class BaseFieldReference extends BaseReference implements FieldReference { @Override public int hashCode() { int hashCode = getDefiningClass().hashCode(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodHandleReference.java b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodHandleReference.java index 8d3b4e7c..752da367 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodHandleReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodHandleReference.java @@ -39,7 +39,7 @@ import org.jf.dexlib2.iface.reference.Reference; import javax.annotation.Nonnull; -public abstract class BaseMethodHandleReference implements MethodHandleReference { +public abstract class BaseMethodHandleReference extends BaseReference implements MethodHandleReference { @Override public int hashCode() { int hashCode = getMethodHandleType(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodProtoReference.java b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodProtoReference.java index 2fc5ed13..5f51c737 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodProtoReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodProtoReference.java @@ -41,7 +41,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -public abstract class BaseMethodProtoReference implements MethodProtoReference { +public abstract class BaseMethodProtoReference extends BaseReference implements MethodProtoReference { @Override public int hashCode() { int hashCode = getReturnType().hashCode(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodReference.java b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodReference.java index f297760e..5da96ac0 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseMethodReference.java @@ -40,7 +40,7 @@ import org.jf.util.CollectionUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public abstract class BaseMethodReference implements MethodReference { +public abstract class BaseMethodReference extends BaseReference implements MethodReference { @Override public int hashCode() { int hashCode = getDefiningClass().hashCode(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseReference.java b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseReference.java new file mode 100644 index 00000000..f526fd0a --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseReference.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.base.reference; + +import org.jf.dexlib2.iface.reference.Reference; + +public abstract class BaseReference implements Reference { + @Override + public void validateReference() throws InvalidReferenceException { + // A reference is valid by default + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseStringReference.java b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseStringReference.java index 2f13c1ae..214c1c86 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseStringReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseStringReference.java @@ -36,7 +36,7 @@ import org.jf.dexlib2.iface.reference.StringReference; import javax.annotation.Nonnull; import javax.annotation.Nullable; -public abstract class BaseStringReference implements StringReference { +public abstract class BaseStringReference extends BaseReference implements StringReference { @Override public int hashCode() { return getString().hashCode(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseTypeReference.java b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseTypeReference.java index 4b24d071..867b75c1 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseTypeReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/base/reference/BaseTypeReference.java @@ -35,7 +35,7 @@ import org.jf.dexlib2.iface.reference.TypeReference; import javax.annotation.Nonnull; -public abstract class BaseTypeReference implements TypeReference { +public abstract class BaseTypeReference extends BaseReference implements TypeReference { @Override public int hashCode() { return getType().hashCode(); diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction20bc.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction20bc.java index 5f86a0ed..44a5f829 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction20bc.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/instruction/DexBackedInstruction20bc.java @@ -52,8 +52,19 @@ public class DexBackedInstruction20bc extends DexBackedInstruction implements In @Nonnull @Override public Reference getReference() { - int referenceType = getReferenceType(); - return DexBackedReference.makeReference(dexFile, referenceType, dexFile.readUshort(instructionStart + 2)); + int referenceIndex = dexFile.readUshort(instructionStart + 2); + try { + int referenceType = getReferenceType(); + return DexBackedReference.makeReference(dexFile, referenceType, referenceIndex); + } catch (ReferenceType.InvalidReferenceTypeException ex) { + return new Reference() { + @Override + public void validateReference() throws InvalidReferenceException { + throw new InvalidReferenceException(String.format("%d@%d", ex.getReferenceType(), referenceIndex), + ex); + } + }; + } } @Override public int getReferenceType() { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedCallSiteReference.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedCallSiteReference.java index 9e825d69..54d3ac70 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedCallSiteReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedCallSiteReference.java @@ -157,4 +157,11 @@ public class DexBackedCallSiteReference extends BaseCallSiteReference { } return callSiteOffset; } + + @Override + public void validateReference() throws InvalidReferenceException { + if (callSiteIndex < 0 || callSiteIndex >= dexFile.getCallSiteCount()) { + throw new InvalidReferenceException("callsite@" + callSiteIndex); + } + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedFieldReference.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedFieldReference.java index a00c987a..35576653 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedFieldReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedFieldReference.java @@ -39,29 +39,30 @@ import javax.annotation.Nonnull; public class DexBackedFieldReference extends BaseFieldReference { @Nonnull public final DexBackedDexFile dexFile; - public final int fieldIdItemOffset; + private final int fieldIndex; public DexBackedFieldReference(@Nonnull DexBackedDexFile dexFile, int fieldIndex) { this.dexFile = dexFile; - this.fieldIdItemOffset = dexFile.getFieldIdItemOffset(fieldIndex); + this.fieldIndex = fieldIndex; } @Nonnull @Override public String getDefiningClass() { - return dexFile.getType(dexFile.readUshort(fieldIdItemOffset + FieldIdItem.CLASS_OFFSET)); + return dexFile.getType(dexFile.readUshort(dexFile.getFieldIdItemOffset(fieldIndex) + FieldIdItem.CLASS_OFFSET)); } @Nonnull @Override public String getName() { - return dexFile.getString(dexFile.readSmallUint(fieldIdItemOffset + FieldIdItem.NAME_OFFSET)); + return dexFile.getString(dexFile.readSmallUint(dexFile.getFieldIdItemOffset(fieldIndex) + + FieldIdItem.NAME_OFFSET)); } @Nonnull @Override public String getType() { - return dexFile.getType(dexFile.readUshort(fieldIdItemOffset + FieldIdItem.TYPE_OFFSET)); + return dexFile.getType(dexFile.readUshort(dexFile.getFieldIdItemOffset(fieldIndex) + FieldIdItem.TYPE_OFFSET)); } /** @@ -74,4 +75,11 @@ public class DexBackedFieldReference extends BaseFieldReference { public int getSize() { return FieldIdItem.ITEM_SIZE; } + + @Override + public void validateReference() throws InvalidReferenceException { + if (fieldIndex < 0 || fieldIndex >= dexFile.getFieldCount()) { + throw new InvalidReferenceException("field@" + fieldIndex); + } + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodHandleReference.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodHandleReference.java index 11bb33bb..58314dc7 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodHandleReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodHandleReference.java @@ -76,4 +76,17 @@ public class DexBackedMethodHandleReference extends BaseMethodHandleReference { throw new ExceptionWithContext("Invalid method handle type: %d", getMethodHandleType()); } } + + @Override + public void validateReference() throws InvalidReferenceException { + if (methodHandleIndex < 0 || methodHandleIndex >= dexFile.getMethodHandleCount()) { + throw new InvalidReferenceException("methodhandle@" + methodHandleIndex); + } + + try { + getMemberReference(); + } catch (ExceptionWithContext ex) { + throw new InvalidReferenceException("methodhandle@" + methodHandleIndex, ex); + } + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodProtoReference.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodProtoReference.java index 9d9c6b18..bdc2c41b 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodProtoReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodProtoReference.java @@ -38,22 +38,23 @@ import org.jf.dexlib2.dexbacked.raw.ProtoIdItem; import org.jf.dexlib2.dexbacked.raw.TypeListItem; import org.jf.dexlib2.dexbacked.util.FixedSizeList; -import java.util.List; import javax.annotation.Nonnull; +import java.util.List; public class DexBackedMethodProtoReference extends BaseMethodProtoReference { @Nonnull public final DexBackedDexFile dexFile; - private final int protoIdItemOffset; + private final int protoIndex; public DexBackedMethodProtoReference(@Nonnull DexBackedDexFile dexFile, int protoIndex) { this.dexFile = dexFile; - this.protoIdItemOffset = dexFile.getProtoIdItemOffset(protoIndex); + this.protoIndex = protoIndex; } @Nonnull @Override public List getParameterTypes() { - final int parametersOffset = dexFile.readSmallUint(protoIdItemOffset + ProtoIdItem.PARAMETERS_OFFSET); + final int parametersOffset = dexFile.readSmallUint(dexFile.getProtoIdItemOffset(protoIndex) + + ProtoIdItem.PARAMETERS_OFFSET); if (parametersOffset > 0) { final int parameterCount = dexFile.readSmallUint(parametersOffset + TypeListItem.SIZE_OFFSET); final int paramListStart = parametersOffset + TypeListItem.LIST_OFFSET; @@ -72,7 +73,8 @@ public class DexBackedMethodProtoReference extends BaseMethodProtoReference { @Nonnull @Override public String getReturnType() { - return dexFile.getType(dexFile.readSmallUint(protoIdItemOffset + ProtoIdItem.RETURN_TYPE_OFFSET)); + return dexFile.getType(dexFile.readSmallUint(dexFile.getProtoIdItemOffset(protoIndex) + + ProtoIdItem.RETURN_TYPE_OFFSET)); } /** @@ -90,4 +92,11 @@ public class DexBackedMethodProtoReference extends BaseMethodProtoReference { } return size; } + + @Override + public void validateReference() throws InvalidReferenceException { + if (protoIndex < 0 || protoIndex >= dexFile.getProtoCount()) { + throw new InvalidReferenceException("proto@" + protoIndex); + } + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodReference.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodReference.java index bee08f8c..775e8d6a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedMethodReference.java @@ -44,24 +44,26 @@ import java.util.List; public class DexBackedMethodReference extends BaseMethodReference { @Nonnull public final DexBackedDexFile dexFile; - public final int methodIdItemOffset; + private final int methodIndex; private int protoIdItemOffset; public DexBackedMethodReference(@Nonnull DexBackedDexFile dexFile, int methodIndex) { this.dexFile = dexFile; - this.methodIdItemOffset = dexFile.getMethodIdItemOffset(methodIndex); + this.methodIndex = methodIndex; } @Nonnull @Override public String getDefiningClass() { - return dexFile.getType(dexFile.readUshort(methodIdItemOffset + MethodIdItem.CLASS_OFFSET)); + return dexFile.getType(dexFile.readUshort(dexFile.getMethodIdItemOffset(methodIndex) + + MethodIdItem.CLASS_OFFSET)); } @Nonnull @Override public String getName() { - return dexFile.getString(dexFile.readSmallUint(methodIdItemOffset + MethodIdItem.NAME_OFFSET)); + return dexFile.getString(dexFile.readSmallUint(dexFile.getMethodIdItemOffset(methodIndex) + + MethodIdItem.NAME_OFFSET)); } @Nonnull @@ -94,7 +96,7 @@ public class DexBackedMethodReference extends BaseMethodReference { private int getProtoIdItemOffset() { if (protoIdItemOffset == 0) { protoIdItemOffset = dexFile.getProtoIdItemOffset( - dexFile.readUshort(methodIdItemOffset + MethodIdItem.PROTO_OFFSET)); + dexFile.readUshort(dexFile.getMethodIdItemOffset(methodIndex) + MethodIdItem.PROTO_OFFSET)); } return protoIdItemOffset; } @@ -109,4 +111,11 @@ public class DexBackedMethodReference extends BaseMethodReference { public int getSize() { return MethodIdItem.ITEM_SIZE; //ushort + ushort + uint for indices } + + @Override + public void validateReference() throws InvalidReferenceException { + if (methodIndex < 0 || methodIndex >= dexFile.getMethodCount()) { + throw new InvalidReferenceException("method@" + methodIndex); + } + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedStringReference.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedStringReference.java index 4eb0097d..d1ed0547 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedStringReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedStringReference.java @@ -73,4 +73,11 @@ public class DexBackedStringReference extends BaseStringReference { size += reader.peekStringLength(utf16Length); return size; } + + @Override + public void validateReference() throws InvalidReferenceException { + if (stringIndex < 0 || stringIndex >= dexFile.getStringCount()) { + throw new InvalidReferenceException("string@" + stringIndex); + } + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedTypeReference.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedTypeReference.java index 5dc52410..3b952a46 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedTypeReference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/reference/DexBackedTypeReference.java @@ -62,4 +62,11 @@ public class DexBackedTypeReference extends BaseTypeReference { public int getSize() { return TypeIdItem.ITEM_SIZE; //uint for descriptor_idx } + + @Override + public void validateReference() throws InvalidReferenceException { + if (typeIndex < 0 || typeIndex >= dexFile.getTypeCount()) { + throw new InvalidReferenceException("type@" + typeIndex); + } + } } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/reference/Reference.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/reference/Reference.java index 1fa93a03..e1ba73ff 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/iface/reference/Reference.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/reference/Reference.java @@ -32,8 +32,49 @@ package org.jf.dexlib2.iface.reference; /** - * This class is the base interface for field/method/string/type references in a dex file. It has no functionality or - * contract itself. + * This class is the base interface for field/method/string/type references in a dex file. */ public interface Reference { + + /** + * Verifies that this reference is valid. + * + * @throws InvalidReferenceException If the reference is not valid. + */ + void validateReference() throws InvalidReferenceException; + + class InvalidReferenceException extends Exception { + private final String invalidReferenceRepresentation; + + public InvalidReferenceException(String invalidReferenceRepresentation) { + super("Invalid reference"); + this.invalidReferenceRepresentation = invalidReferenceRepresentation; + } + + public InvalidReferenceException(String invalidReferenceRepresentation, String msg) { + super(msg); + this.invalidReferenceRepresentation = invalidReferenceRepresentation; + } + + public InvalidReferenceException(String invalidReferenceRepresentation, String s, Throwable throwable) { + super(s, throwable); + this.invalidReferenceRepresentation = invalidReferenceRepresentation; + } + + public InvalidReferenceException(String invalidReferenceRepresentation, Throwable throwable) { + super(throwable); + this.invalidReferenceRepresentation = invalidReferenceRepresentation; + } + + /** + * @return A string representation of the invalid reference. This should be a human-readable string that gives + * enough information to identify the reference in question. + * + * The format of the string is not specified, although as an illustrative example "string@123" could be + * used for a reference to the string at index 123. + */ + public String getInvalidReferenceRepresentation() { + return invalidReferenceRepresentation; + } + } }