Added second pass which does code flow checking to verifier.
Ran this through libcore and it finds no errors, but I still need to
create tests to make sure it catches errors when it should. Also, it's
still missing 2 pieces, replacement of failing opcodes and generation of
the register map.
Change-Id: I0f4c4c20751b5b030ca44c23e1d1c2e133404e0c
diff --git a/src/class_linker.cc b/src/class_linker.cc
index f312556..14f4883 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1193,7 +1193,7 @@
CHECK(klass->GetStatus() == Class::kStatusResolved);
klass->SetStatus(Class::kStatusVerifying);
- if (!DexVerify::VerifyClass(klass)) {
+ if (!DexVerifier::VerifyClass(klass)) {
LG << "Verification failed"; // TODO: ThrowVerifyError
Object* exception = self->GetException();
klass->SetVerifyErrorClass(exception->GetClass());
@@ -1592,6 +1592,8 @@
for (size_t i = 0; i < count; ++i) {
klass->GetVirtualMethodDuringLinking(i)->SetMethodIndex(i);
}
+ // Link interface method tables
+ LinkInterfaceMethods(klass);
} else {
// Link virtual method tables
LinkVirtualMethods(klass);
@@ -2103,7 +2105,9 @@
const char* name = dex_file.dexStringById(method_id.name_idx_);
std::string signature(dex_file.CreateMethodDescriptor(method_id.proto_idx_, NULL));
- if (is_direct) {
+ if (klass->IsInterface()) {
+ resolved = klass->FindInterfaceMethod(name, signature);
+ } else if (is_direct) {
resolved = klass->FindDirectMethod(name, signature);
} else {
resolved = klass->FindVirtualMethod(name, signature);
diff --git a/src/dex_instruction.h b/src/dex_instruction.h
index 8cf645d..3934d7d 100644
--- a/src/dex_instruction.h
+++ b/src/dex_instruction.h
@@ -87,6 +87,23 @@
kVerifyError = 0x80000,
};
+ /*
+ * Holds the contents of a decoded instruction.
+ */
+ struct DecodedInstruction {
+ uint32_t vA_;
+ uint32_t vB_;
+ uint64_t vB_wide_; /* for kFmt51l */
+ uint32_t vC_;
+ uint32_t arg_[5]; /* vC/D/E/F/G in invoke or filled-new-array */
+ Code opcode_;
+
+ DecodedInstruction(const Instruction* inst) {
+ inst->Decode(vA_, vB_, vB_wide_, vC_, arg_);
+ opcode_ = inst->Opcode();
+ }
+ };
+
// Decodes this instruction, populating its arguments.
void Decode(uint32_t &vA, uint32_t &vB, uint64_t &vB_wide, uint32_t &vC, uint32_t arg[]) const;
@@ -115,6 +132,11 @@
return kInstructionFormats[Opcode()];
}
+ // Returns the flags for the current instruction.
+ int Flag() const {
+ return kInstructionFlags[Opcode()];
+ }
+
// Returns true if this instruction is a branch.
bool IsBranch() const {
return (kInstructionFlags[Opcode()] & kBranch) != 0;
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index f257e78..86b119b 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -8,100 +8,448 @@
#include "dex_file.h"
#include "dex_instruction.h"
#include "dex_instruction_visitor.h"
+#include "dex_verifier.h"
#include "logging.h"
#include "runtime.h"
#include "stringpiece.h"
+#include "UniquePtr.h"
namespace art {
-/*
- * Returns "true" if the flags indicate that this address holds the start
- * of an instruction.
- */
-inline bool InsnIsOpcode(const uint32_t insn_flags[], int addr) {
- return (insn_flags[addr] & DexVerify::kInsnFlagWidthMask) != 0;
+#define k_ kRegTypeUnknown
+#define kU kRegTypeUninit
+#define kX kRegTypeConflict
+#define k0 kRegTypeZero
+#define k1 kRegTypeOne
+#define kZ kRegTypeBoolean
+#define ky kRegTypeConstPosByte
+#define kY kRegTypeConstByte
+#define kh kRegTypeConstPosShort
+#define kH kRegTypeConstShort
+#define kc kRegTypeConstChar
+#define ki kRegTypeConstInteger
+#define kb kRegTypePosByte
+#define kB kRegTypeByte
+#define ks kRegTypePosShort
+#define kS kRegTypeShort
+#define kC kRegTypeChar
+#define kI kRegTypeInteger
+#define kF kRegTypeFloat
+#define kN kRegTypeConstLo
+#define kn kRegTypeConstHi
+#define kJ kRegTypeLongLo
+#define kj kRegTypeLongHi
+#define kD kRegTypeDoubleLo
+#define kd kRegTypeDoubleHi
+
+const char DexVerifier::merge_table_[kRegTypeMAX][kRegTypeMAX] =
+ {
+ /* chk: _ U X 0 1 Z y Y h H c i b B s S C I F N n J j D d */
+ { /*_*/ k_,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX },
+ { /*U*/ kX,kU,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX },
+ { /*X*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX },
+ { /*0*/ kX,kX,kX,k0,kZ,kZ,ky,kY,kh,kH,kc,ki,kb,kB,ks,kS,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+ { /*1*/ kX,kX,kX,kZ,k1,kZ,ky,kY,kh,kH,kc,ki,kb,kB,ks,kS,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+ { /*Z*/ kX,kX,kX,kZ,kZ,kZ,ky,kY,kh,kH,kc,ki,kb,kB,ks,kS,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+ { /*y*/ kX,kX,kX,ky,ky,ky,ky,kY,kh,kH,kc,ki,kb,kB,ks,kS,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+ { /*Y*/ kX,kX,kX,kY,kY,kY,kY,kY,kh,kH,kc,ki,kB,kB,kS,kS,kI,kI,kF,kX,kX,kX,kX,kX,kX },
+ { /*h*/ kX,kX,kX,kh,kh,kh,kh,kh,kh,kH,kc,ki,ks,kS,ks,kS,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+ { /*H*/ kX,kX,kX,kH,kH,kH,kH,kH,kH,kH,kc,ki,kS,kS,kS,kS,kI,kI,kF,kX,kX,kX,kX,kX,kX },
+ { /*c*/ kX,kX,kX,kc,kc,kc,kc,kc,kc,kc,kc,ki,kC,kI,kC,kI,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+ { /*i*/ kX,kX,kX,ki,ki,ki,ki,ki,ki,ki,ki,ki,kI,kI,kI,kI,kI,kI,kF,kX,kX,kX,kX,kX,kX },
+ { /*b*/ kX,kX,kX,kb,kb,kb,kb,kB,ks,kS,kC,kI,kb,kB,ks,kS,kC,kI,kX,kX,kX,kX,kX,kX,kX },
+ { /*B*/ kX,kX,kX,kB,kB,kB,kB,kB,kS,kS,kI,kI,kB,kB,kS,kS,kI,kI,kX,kX,kX,kX,kX,kX,kX },
+ { /*s*/ kX,kX,kX,ks,ks,ks,ks,kS,ks,kS,kC,kI,ks,kS,ks,kS,kC,kI,kX,kX,kX,kX,kX,kX,kX },
+ { /*S*/ kX,kX,kX,kS,kS,kS,kS,kS,kS,kS,kI,kI,kS,kS,kS,kS,kI,kI,kX,kX,kX,kX,kX,kX,kX },
+ { /*C*/ kX,kX,kX,kC,kC,kC,kC,kI,kC,kI,kC,kI,kC,kI,kC,kI,kC,kI,kX,kX,kX,kX,kX,kX,kX },
+ { /*I*/ kX,kX,kX,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kX,kX,kX,kX,kX,kX,kX },
+ { /*F*/ kX,kX,kX,kF,kF,kF,kF,kF,kF,kF,kF,kF,kX,kX,kX,kX,kX,kX,kF,kX,kX,kX,kX,kX,kX },
+ { /*N*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kN,kX,kJ,kX,kD,kX },
+ { /*n*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kn,kX,kj,kX,kd },
+ { /*J*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kJ,kX,kJ,kX,kX,kX },
+ { /*j*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kj,kX,kj,kX,kX },
+ { /*D*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kD,kX,kX,kX,kD,kX },
+ { /*d*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kd,kX,kX,kX,kd },
+ };
+
+#undef k_
+#undef kU
+#undef kX
+#undef k0
+#undef k1
+#undef kZ
+#undef ky
+#undef kY
+#undef kh
+#undef kH
+#undef kc
+#undef ki
+#undef kb
+#undef kB
+#undef ks
+#undef kS
+#undef kC
+#undef kI
+#undef kF
+#undef kN
+#undef kn
+#undef kJ
+#undef kj
+#undef kD
+#undef kd
+
+bool DexVerifier::VerifyClass(Class* klass) {
+ if (klass->IsVerified()) {
+ return true;
+ }
+ for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
+ Method* method = klass->GetDirectMethod(i);
+ if (!VerifyMethod(method)) {
+ LOG(ERROR) << "Verifier rejected class "
+ << klass->GetDescriptor()->ToModifiedUtf8();
+ return false;
+ }
+ }
+ for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
+ Method* method = klass->GetVirtualMethod(i);
+ if (!VerifyMethod(method)) {
+ LOG(ERROR) << "Verifier rejected class "
+ << klass->GetDescriptor()->ToModifiedUtf8();
+ return false;
+ }
+ }
+ return true;
}
-/*
- * Extract the unsigned 16-bit instruction width from "flags".
- */
-inline int InsnGetWidth(const uint32_t insn_flags[], int addr) {
- return insn_flags[addr] & DexVerify::kInsnFlagWidthMask;
+bool DexVerifier::VerifyMethod(Method* method) {
+ const DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
+ const DexFile::CodeItem *code_item =
+ dex_file.GetCodeItem(method->GetCodeItemOffset());
+
+ /*
+ * Construct the verifier state container object.
+ */
+ VerifierData vdata(method, &dex_file, code_item);
+
+ /*
+ * If there aren't any instructions, make sure that's expected, then
+ * exit successfully.
+ */
+ if (code_item == NULL) {
+ if (!method->IsNative() && !method->IsAbstract()) {
+ LOG(ERROR) << "VFY: zero-length code in concrete non-native method";
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * Sanity-check the register counts. ins + locals = registers, so make
+ * sure that ins <= registers.
+ */
+ if (code_item->ins_size_ > code_item->registers_size_) {
+ LOG(ERROR) << "VFY: bad register counts (ins=" << code_item->ins_size_
+ << " regs=" << code_item->registers_size_;
+ return false;
+ }
+
+ /*
+ * Allocate and initialize an array to hold instruction data.
+ */
+ UniquePtr<InsnFlags> insn_flags(new InsnFlags[code_item->insns_size_]());
+ vdata.insn_flags_ = insn_flags.get();
+
+ /*
+ * Run through the instructions and see if the width checks out.
+ */
+ if (!ComputeWidthsAndCountOps(&vdata)) {
+ return false;
+ }
+
+ /*
+ * Flag instructions guarded by a "try" block and check exception handlers.
+ */
+ if (!ScanTryCatchBlocks(&vdata)) {
+ return false;
+ }
+
+ /*
+ * Perform static instruction verification.
+ */
+ if (!VerifyInstructions(&vdata)) {
+ return false;
+ }
+
+ /*
+ * Perform code flow analysis.
+ */
+ if (!VerifyCodeFlow(&vdata)) {
+ return false;
+ }
+
+ return true;
}
-/*
- * Changed?
- */
-inline bool InsnIsChanged(const uint32_t insn_flags[], int addr) {
- return (insn_flags[addr] & DexVerify::kInsnFlagChanged) != 0;
-}
-inline void InsnSetChanged(uint32_t insn_flags[], int addr, bool changed) {
- if (changed)
- insn_flags[addr] |= DexVerify::kInsnFlagChanged;
- else
- insn_flags[addr] &= ~DexVerify::kInsnFlagChanged;
+bool DexVerifier::VerifyInstructions(VerifierData* vdata) {
+ const DexFile::CodeItem* code_item = vdata->code_item_;
+ InsnFlags* insn_flags = vdata->insn_flags_;
+ const byte* ptr = reinterpret_cast<const byte*>(code_item->insns_);
+ const Instruction* inst = Instruction::At(ptr);
+
+ /* Flag the start of the method as a branch target. */
+ InsnSetBranchTarget(insn_flags, 0);
+
+ uint32_t width = 0;
+ uint32_t insns_size = code_item->insns_size_;
+
+ while (width < insns_size) {
+ if (!VerifyInstruction(vdata, inst, width)) {
+ LOG(ERROR) << "VFY: rejecting opcode 0x" << std::hex
+ << (int) inst->Opcode() << " at 0x" << width << std::dec;
+ return false;
+ }
+
+ /* Flag instructions that are garbage collection points */
+ if (inst->IsBranch() || inst->IsSwitch() || inst->IsThrow() ||
+ inst->IsReturn()) {
+ InsnSetGcPoint(insn_flags, width);
+ }
+
+ width += inst->Size();
+ inst = inst->Next();
+ }
+ return true;
}
-/*
- * Visited?
- */
-inline bool InsnIsVisited(const uint32_t insn_flags[], int addr) {
- return (insn_flags[addr] & DexVerify::kInsnFlagVisited) != 0;
-}
-inline void InsnSetVisited(uint32_t insn_flags[], int addr, bool changed) {
- if (changed)
- insn_flags[addr] |= DexVerify::kInsnFlagVisited;
- else
- insn_flags[addr] &= ~DexVerify::kInsnFlagVisited;
+bool DexVerifier::VerifyInstruction(VerifierData* vdata,
+ const Instruction* inst, uint32_t code_offset) {
+ const DexFile* dex_file = vdata->dex_file_;
+ const DexFile::CodeItem* code_item = vdata->code_item_;
+ InsnFlags* insn_flags = vdata->insn_flags_;
+ Instruction::DecodedInstruction dec_insn(inst);
+ bool result = true;
+
+ int argumentA = inst->GetVerifyTypeArgumentA();
+ int argumentB = inst->GetVerifyTypeArgumentB();
+ int argumentC = inst->GetVerifyTypeArgumentC();
+ int extra_flags = inst->GetVerifyExtraFlags();
+
+ switch (argumentA) {
+ case Instruction::kVerifyRegA:
+ result &= CheckRegisterIndex(code_item, dec_insn.vA_);
+ break;
+ case Instruction::kVerifyRegAWide:
+ result &= CheckWideRegisterIndex(code_item, dec_insn.vA_);
+ break;
+ }
+
+ switch (argumentB) {
+ case Instruction::kVerifyRegB:
+ result &= CheckRegisterIndex(code_item, dec_insn.vB_);
+ break;
+ case Instruction::kVerifyRegBField:
+ result &= CheckFieldIndex(dex_file, dec_insn.vB_);
+ break;
+ case Instruction::kVerifyRegBMethod:
+ result &= CheckMethodIndex(dex_file, dec_insn.vB_);
+ break;
+ case Instruction::kVerifyRegBNewInstance:
+ result &= CheckNewInstance(dex_file, dec_insn.vB_);
+ break;
+ case Instruction::kVerifyRegBString:
+ result &= CheckStringIndex(dex_file, dec_insn.vB_);
+ break;
+ case Instruction::kVerifyRegBType:
+ result &= CheckTypeIndex(dex_file, dec_insn.vB_);
+ break;
+ case Instruction::kVerifyRegBWide:
+ result &= CheckWideRegisterIndex(code_item, dec_insn.vB_);
+ break;
+ }
+
+ switch (argumentC) {
+ case Instruction::kVerifyRegC:
+ result &= CheckRegisterIndex(code_item, dec_insn.vC_);
+ break;
+ case Instruction::kVerifyRegCField:
+ result &= CheckFieldIndex(dex_file, dec_insn.vC_);
+ break;
+ case Instruction::kVerifyRegCNewArray:
+ result &= CheckNewArray(dex_file, dec_insn.vC_);
+ break;
+ case Instruction::kVerifyRegCType:
+ result &= CheckTypeIndex(dex_file, dec_insn.vC_);
+ break;
+ case Instruction::kVerifyRegCWide:
+ result &= CheckWideRegisterIndex(code_item, dec_insn.vC_);
+ break;
+ }
+
+ switch (extra_flags) {
+ case Instruction::kVerifyArrayData:
+ result &= CheckArrayData(code_item, code_offset);
+ break;
+ case Instruction::kVerifyBranchTarget:
+ result &= CheckBranchTarget(code_item, insn_flags, code_offset);
+ break;
+ case Instruction::kVerifySwitchTargets:
+ result &= CheckSwitchTargets(code_item, insn_flags, code_offset);
+ break;
+ case Instruction::kVerifyVarArg:
+ result &= CheckVarArgRegs(code_item, dec_insn.vA_, dec_insn.arg_);
+ break;
+ case Instruction::kVerifyVarArgRange:
+ result &= CheckVarArgRangeRegs(code_item, dec_insn.vA_, dec_insn.vC_);
+ break;
+ case Instruction::kVerifyError:
+ LOG(ERROR) << "VFY: unexpected opcode " << std::hex
+ << (int) dec_insn.opcode_ << std::dec;
+ result = false;
+ break;
+ }
+
+ return result;
}
-/*
- * Visited or changed?
- */
-inline bool InsnIsVisitedOrChanged(const uint32_t insn_flags[], int addr) {
- return (insn_flags[addr] & (DexVerify::kInsnFlagVisited |
- DexVerify::kInsnFlagChanged)) != 0;
-}
+bool DexVerifier::VerifyCodeFlow(VerifierData* vdata) {
+ Method* method = vdata->method_;
+ const DexFile::CodeItem* code_item = vdata->code_item_;
+ uint16_t registers_size = code_item->registers_size_;
+ uint32_t insns_size = code_item->insns_size_;
+ RegisterTable reg_table;
-/*
- * In a "try" block?
- */
-inline bool InsnIsInTry(const uint32_t insn_flags[], int addr) {
- return (insn_flags[addr] & DexVerify::kInsnFlagInTry) != 0;
-}
-inline void InsnSetInTry(uint32_t insn_flags[], int addr, bool inTry) {
- insn_flags[addr] |= DexVerify::kInsnFlagInTry;
-}
+ if (registers_size * insns_size > 4*1024*1024) {
+ LOG(ERROR) << "VFY: warning: method is huge (regs=" << registers_size
+ << " insns_size=" << insns_size << ")";
+ }
-/*
- * Instruction is a branch target or exception handler?
- */
-inline bool InsnIsBranchTarget(const uint32_t insn_flags[], int addr) {
- return (insn_flags[addr] & DexVerify::kInsnFlagBranchTarget) != 0;
-}
-inline void InsnSetBranchTarget(uint32_t insn_flags[], int addr, bool isBranch)
-{
- insn_flags[addr] |= DexVerify::kInsnFlagBranchTarget;
-}
+ /* Create and initialize register lists. */
+ if (!InitRegisterTable(vdata, ®_table, kTrackRegsGcPoints)) {
+ return false;
+ }
-/*
- * Instruction is a GC point?
- */
-inline bool InsnIsGcPoint(const uint32_t insn_flags[], int addr) {
- return (insn_flags[addr] & DexVerify::kInsnFlagGcPoint) != 0;
-}
-inline void InsnSetGcPoint(uint32_t insn_flags[], int addr, bool isGcPoint) {
- insn_flags[addr] |= DexVerify::kInsnFlagGcPoint;
-}
+ vdata->register_lines_ = reg_table.register_lines_;
+
+ /* Allocate a map to hold the classes of uninitialized instances. */
+ UniquePtr<UninitInstanceMap> uninit_map(CreateUninitInstanceMap(vdata));
+ vdata->uninit_map_ = uninit_map.get();
+
+ /* Initialize register types of method arguments. */
+ if (!SetTypesFromSignature(vdata, reg_table.register_lines_[0].reg_types_)) {
+ LOG(ERROR) << "VFY: bad signature '"
+ << method->GetSignature()->ToModifiedUtf8() << "' for "
+ << method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+ << "." << method->GetName()->ToModifiedUtf8();
+ return false;
+ }
+
+ /* Perform code flow verification. */
+ if (!CodeFlowVerifyMethod(vdata, ®_table)) {
+ return false;
+ }
+
+ /* TODO: Generate a register map. */
-/*
- * Extract the relative offset from a branch instruction.
- *
- * Returns "false" on failure (e.g. this isn't a branch instruction).
- */
-bool GetBranchOffset(const DexFile::CodeItem* code_item,
- const uint32_t insn_flags[], int cur_offset, int32_t* pOffset,
+ return true;
+}
+
+bool DexVerifier::ComputeWidthsAndCountOps(VerifierData* vdata) {
+ const uint16_t* insns = vdata->code_item_->insns_;
+ uint32_t insns_size = vdata->code_item_->insns_size_;
+ InsnFlags* insn_flags = vdata->insn_flags_;
+ const byte* ptr = reinterpret_cast<const byte*>(insns);
+ const Instruction* inst = Instruction::At(ptr);
+ size_t new_instance_count = 0;
+ size_t monitor_enter_count = 0;
+ size_t width = 0;
+
+ while (width < insns_size) {
+ Instruction::Code opcode = inst->Opcode();
+ if (opcode == Instruction::NEW_INSTANCE) {
+ new_instance_count++;
+ } else if (opcode == Instruction::MONITOR_ENTER) {
+ monitor_enter_count++;
+ }
+
+ insn_flags[width] |= inst->Size();
+ width += inst->Size();
+ inst = inst->Next();
+ }
+
+ if (width != insns_size) {
+ LOG(ERROR) << "VFY: code did not end where expected (" << width << " vs. "
+ << insns_size << ")";
+ return false;
+ }
+
+ vdata->new_instance_count_ = new_instance_count;
+ vdata->monitor_enter_count_ = monitor_enter_count;
+ return true;
+}
+
+bool DexVerifier::ScanTryCatchBlocks(VerifierData* vdata) {
+ const DexFile::CodeItem* code_item = vdata->code_item_;
+ InsnFlags* insn_flags = vdata->insn_flags_;
+ uint32_t insns_size = code_item->insns_size_;
+ uint32_t tries_size = code_item->tries_size_;
+
+ if (tries_size == 0) {
+ return true;
+ }
+
+ const DexFile::TryItem* tries = DexFile::dexGetTryItems(*code_item, 0);
+
+ for (uint32_t idx = 0; idx < tries_size; idx++) {
+ const DexFile::TryItem* try_item = &tries[idx];
+ uint32_t start = try_item->start_addr_;
+ uint32_t end = start + try_item->insn_count_;
+
+ if ((start >= end) || (start >= insns_size) || (end > insns_size)) {
+ LOG(ERROR) << "VFY: bad exception entry: startAddr=" << start
+ << " endAddr=" << end << " (size=" << insns_size << ")";
+ return false;
+ }
+
+ if (InsnGetWidth(insn_flags, start) == 0) {
+ LOG(ERROR) << "VFY: 'try' block starts inside an instruction ("
+ << start << ")";
+ return false;
+ }
+
+ uint32_t addr;
+ for (addr = start; addr < end; addr += InsnGetWidth(insn_flags, addr)) {
+ InsnSetInTry(insn_flags, addr);
+ }
+ }
+
+ /* Iterate over each of the handlers to verify target addresses. */
+ const byte* handlers_ptr = DexFile::dexGetCatchHandlerData(*code_item, 0);
+ uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+ for (uint32_t idx = 0; idx < handlers_size; idx++) {
+ DexFile::CatchHandlerIterator iterator(handlers_ptr);
+
+ for (; !iterator.HasNext(); iterator.Next()) {
+ uint32_t addr = iterator.Get().address_;
+ if (InsnGetWidth(insn_flags, addr) == 0) {
+ LOG(ERROR) << "VFY: exception handler starts at bad address ("
+ << addr << ")";
+ return false;
+ }
+
+ InsnSetBranchTarget(insn_flags, addr);
+ }
+
+ handlers_ptr = iterator.GetData();
+ }
+
+ return true;
+}
+
+bool DexVerifier::GetBranchOffset(const DexFile::CodeItem* code_item,
+ const InsnFlags insn_flags[], uint32_t cur_offset, int32_t* pOffset,
bool* pConditional, bool* selfOkay) {
const uint16_t* insns = code_item->insns_ + cur_offset;
@@ -145,12 +493,7 @@
return true;
}
-
-/*
- * Verify an array data table. "cur_offset" is the offset of the
- * fill-array-data instruction.
- */
-static bool CheckArrayData(const DexFile::CodeItem* code_item,
+bool DexVerifier::CheckArrayData(const DexFile::CodeItem* code_item,
uint32_t cur_offset) {
const uint32_t insn_count = code_item->insns_size_;
const uint16_t* insns = code_item->insns_ + cur_offset;
@@ -196,13 +539,7 @@
return true;
}
-/*
- * Perform static checks on a "new-instance" instruction. Specifically,
- * make sure the class reference isn't for an array class.
- *
- * We don't need the actual class, just a pointer to the class name.
- */
-static bool CheckNewInstance(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckNewInstance(const DexFile* dex_file, uint32_t idx) {
if (idx >= dex_file->GetHeader().type_ids_size_) {
LOG(ERROR) << "VFY: bad type index " << idx << " (max "
<< dex_file->GetHeader().type_ids_size_ << ")";
@@ -219,12 +556,7 @@
return true;
}
-/*
- * Perform static checks on a "new-array" instruction. Specifically, make
- * sure they aren't creating an array of arrays that causes the number of
- * dimensions to exceed 255.
- */
-static bool CheckNewArray(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckNewArray(const DexFile* dex_file, uint32_t idx) {
if (idx >= dex_file->GetHeader().type_ids_size_) {
LOG(ERROR) << "VFY: bad type index " << idx << " (max "
<< dex_file->GetHeader().type_ids_size_ << ")";
@@ -252,11 +584,7 @@
return true;
}
-/*
- * Perform static checks on an instruction that takes a class constant.
- * Ensure that the class index is in the valid range.
- */
-static bool CheckTypeIndex(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckTypeIndex(const DexFile* dex_file, uint32_t idx) {
if (idx >= dex_file->GetHeader().type_ids_size_) {
LOG(ERROR) << "VFY: bad type index " << idx << " (max "
<< dex_file->GetHeader().type_ids_size_ << ")";
@@ -265,11 +593,7 @@
return true;
}
-/*
- * Perform static checks on a field get or set instruction. All we do
- * here is ensure that the field index is in the valid range.
- */
-static bool CheckFieldIndex(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckFieldIndex(const DexFile* dex_file, uint32_t idx) {
if (idx >= dex_file->GetHeader().field_ids_size_) {
LOG(ERROR) << "VFY: bad field index " << idx << " (max "
<< dex_file->GetHeader().field_ids_size_ << ")";
@@ -278,11 +602,7 @@
return true;
}
-/*
- * Perform static checks on a method invocation instruction. All we do
- * here is ensure that the method index is in the valid range.
- */
-static bool CheckMethodIndex(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckMethodIndex(const DexFile* dex_file, uint32_t idx) {
if (idx >= dex_file->GetHeader().method_ids_size_) {
LOG(ERROR) << "VFY: bad method index " << idx << " (max "
<< dex_file->GetHeader().method_ids_size_ << ")";
@@ -291,10 +611,7 @@
return true;
}
-/*
- * Ensure that the string index is in the valid range.
- */
-static bool CheckStringIndex(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckStringIndex(const DexFile* dex_file, uint32_t idx) {
if (idx >= dex_file->GetHeader().string_ids_size_) {
LOG(ERROR) << "VFY: bad string index " << idx << " (max "
<< dex_file->GetHeader().string_ids_size_ << ")";
@@ -303,11 +620,8 @@
return true;
}
-/*
- * Ensure that the register index is valid for this code item.
- */
-static bool CheckRegisterIndex(const DexFile::CodeItem* code_item, uint32_t idx)
-{
+bool DexVerifier::CheckRegisterIndex(const DexFile::CodeItem* code_item,
+ uint32_t idx) {
if (idx >= code_item->registers_size_) {
LOG(ERROR) << "VFY: register index out of range (" << idx << " >= "
<< code_item->registers_size_ << ")";
@@ -316,10 +630,7 @@
return true;
}
-/*
- * Ensure that the wide register index is valid for this code item.
- */
-static bool CheckWideRegisterIndex(const DexFile::CodeItem* code_item,
+bool DexVerifier::CheckWideRegisterIndex(const DexFile::CodeItem* code_item,
uint32_t idx) {
if (idx + 1 >= code_item->registers_size_) {
LOG(ERROR) << "VFY: wide register index out of range (" << idx
@@ -329,19 +640,8 @@
return true;
}
-/*
- * Check the register indices used in a "vararg" instruction, such as
- * invoke-virtual or filled-new-array.
- *
- * vA holds word count (0-5), args[] have values.
- *
- * There are some tests we don't do here, e.g. we don't try to verify
- * that invoking a method that takes a double is done with consecutive
- * registers. This requires parsing the target method signature, which
- * we will be doing later on during the code flow analysis.
- */
-static bool CheckVarArgRegs(const DexFile::CodeItem* code_item, uint32_t vA,
- uint32_t arg[]) {
+bool DexVerifier::CheckVarArgRegs(const DexFile::CodeItem* code_item,
+ uint32_t vA, uint32_t arg[]) {
uint16_t registers_size = code_item->registers_size_;
uint32_t idx;
@@ -361,13 +661,7 @@
return true;
}
-/*
- * Check the register indices used in a "vararg/range" instruction, such as
- * invoke-virtual/range or filled-new-array/range.
- *
- * vA holds word count, vC holds index of first reg.
- */
-static bool CheckVarArgRangeRegs(const DexFile::CodeItem* code_item,
+bool DexVerifier::CheckVarArgRangeRegs(const DexFile::CodeItem* code_item,
uint32_t vA, uint32_t vC) {
uint16_t registers_size = code_item->registers_size_;
@@ -384,13 +678,8 @@
return true;
}
-/*
- * Verify a switch table. "cur_offset" is the offset of the switch instruction.
- *
- * Updates "insnFlags", setting the "branch target" flag.
- */
-static bool CheckSwitchTargets(const DexFile::CodeItem* code_item,
- uint32_t insn_flags[], uint32_t cur_offset) {
+bool DexVerifier::CheckSwitchTargets(const DexFile::CodeItem* code_item,
+ InsnFlags insn_flags[], uint32_t cur_offset) {
const uint32_t insn_count = code_item->insns_size_;
const uint16_t* insns = code_item->insns_ + cur_offset;
const uint16_t* switch_insns;
@@ -436,8 +725,9 @@
table_size = targets_offset + switch_count * 2;
if (switch_insns[0] != expected_signature) {
- LOG(ERROR) << "VFY: wrong signature for switch table (0x" << switch_insns[0]
- << ", wanted 0x" << expected_signature << ")";
+ LOG(ERROR) << "VFY: wrong signature for switch table (0x" << std::hex
+ << switch_insns[0] << ", wanted 0x" << expected_signature << ")"
+ << std::dec;
return false;
}
@@ -478,30 +768,19 @@
if (abs_offset < 0 || abs_offset >= (int32_t) insn_count ||
!InsnIsOpcode(insn_flags, abs_offset)) {
LOG(ERROR) << "VFY: invalid switch target " << offset << " (-> "
- << abs_offset << ") at " << cur_offset << "[" << targ << "]";
+ << std::hex << abs_offset << ") at " << cur_offset << std::dec
+ << "[" << targ << "]";
return false;
}
- InsnSetBranchTarget(insn_flags, abs_offset, true);
+ InsnSetBranchTarget(insn_flags, abs_offset);
}
return true;
}
-/*
- * Verify that the target of a branch instruction is valid.
- *
- * We don't expect code to jump directly into an exception handler, but
- * it's valid to do so as long as the target isn't a "move-exception"
- * instruction. We verify that in a later stage.
- *
- * The VM spec doesn't forbid an instruction from branching to itself,
- * but the Dalvik spec declares that only certain instructions can do so.
- *
- * Updates "insnFlags", setting the "branch target" flag.
- */
-static bool CheckBranchTarget(const DexFile::CodeItem* code_item,
- uint32_t insn_flags[], int cur_offset) {
- const int insn_count = code_item->insns_size_;
+bool DexVerifier::CheckBranchTarget(const DexFile::CodeItem* code_item,
+ InsnFlags insn_flags[], uint32_t cur_offset) {
+ const uint32_t insn_count = code_item->insns_size_;
int32_t offset, abs_offset;
bool isConditional, selfOkay;
@@ -510,7 +789,8 @@
return false;
if (!selfOkay && offset == 0) {
- LOG(ERROR) << "VFY: branch offset of zero not allowed at" << cur_offset;
+ LOG(ERROR) << "VFY: branch offset of zero not allowed at" << std::hex
+ << cur_offset << std::dec;
return false;
}
@@ -520,318 +800,4435 @@
* it's unwise to depend on that.
*/
if (((int64_t) cur_offset + (int64_t) offset) !=
- (int64_t)(cur_offset + offset)) {
- LOG(ERROR) << "VFY: branch target overflow " << cur_offset << " +"
- << offset;
+ (int64_t) (cur_offset + offset)) {
+ LOG(ERROR) << "VFY: branch target overflow " << std::hex << cur_offset
+ << std::dec << " +" << offset;
return false;
}
abs_offset = cur_offset + offset;
- if (abs_offset < 0 || abs_offset >= insn_count ||
+ if (abs_offset < 0 || (uint32_t) abs_offset >= insn_count ||
!InsnIsOpcode(insn_flags, abs_offset))
{
LOG(ERROR) << "VFY: invalid branch target " << offset << " (-> "
- << abs_offset << ") at " << cur_offset;
+ << std::hex << abs_offset << ") at " << cur_offset << std::dec;
return false;
}
- InsnSetBranchTarget(insn_flags, abs_offset, true);
+ InsnSetBranchTarget(insn_flags, abs_offset);
return true;
}
-bool CheckInsnWidth(const uint16_t* insns, uint32_t insns_size,
- uint32_t insn_flags[]) {
- const byte* ptr = reinterpret_cast<const byte*>(insns);
+bool DexVerifier::InitRegisterTable(VerifierData* vdata,
+ RegisterTable* reg_table, RegisterTrackingMode track_regs_for) {
+ const DexFile::CodeItem* code_item = vdata->code_item_;
+ InsnFlags* insn_flags = vdata->insn_flags_;
+ uint16_t registers_size = code_item->registers_size_;
+ uint32_t insns_size = code_item->insns_size_;
+ uint32_t i;
+
+ /*
+ * Every address gets a RegisterLine struct. This is wasteful, but
+ * not so much that it's worth chasing through an extra level of
+ * indirection.
+ */
+ reg_table->insn_reg_count_plus_ = registers_size + kExtraRegs;
+ reg_table->register_lines_ = new RegisterLine[insns_size]();
+
+ assert(insns_size > 0);
+
+ bool track_monitors;
+ //if (gDvm.monitorVerification) {
+ //track_monitors = (vdata->monitor_enter_count_ != 0);
+ //} else {
+ track_monitors = false;
+ //}
+
+ /*
+ * Allocate entries in the sparse register line table.
+ *
+ * There is a RegisterLine associated with every address, but not
+ * every RegisterLine has non-NULL pointers to storage for its fields.
+ */
+ for (i = 0; i < insns_size; i++) {
+ bool interesting;
+
+ switch (track_regs_for) {
+ case kTrackRegsAll:
+ interesting = InsnIsOpcode(insn_flags, i);
+ break;
+ case kTrackRegsGcPoints:
+ interesting = InsnIsGcPoint(insn_flags, i) ||
+ InsnIsBranchTarget(insn_flags, i);
+ break;
+ case kTrackRegsBranches:
+ interesting = InsnIsBranchTarget(insn_flags, i);
+ break;
+ default:
+ return false;
+ }
+
+ if (interesting) {
+ reg_table->register_lines_[i].Alloc(reg_table->insn_reg_count_plus_,
+ track_monitors);
+ }
+ }
+
+ /*
+ * Allocate space for our "temporary" register lines.
+ */
+ reg_table->work_line_.Alloc(reg_table->insn_reg_count_plus_, track_monitors);
+ reg_table->saved_line_.Alloc(reg_table->insn_reg_count_plus_, track_monitors);
+
+ return true;
+}
+
+DexVerifier::UninitInstanceMap* DexVerifier::CreateUninitInstanceMap(
+ VerifierData* vdata) {
+ Method* method = vdata->method_;
+ const DexFile::CodeItem* code_item = vdata->code_item_;
+ size_t new_instance_count = vdata->new_instance_count_;
+
+ if (IsInitMethod(method)) {
+ new_instance_count++;
+ }
+
+ /*
+ * Allocate the header and map as a single unit.
+ *
+ * TODO: consider having a static instance so we can avoid allocations.
+ * I don't think the verifier is guaranteed to be single-threaded when
+ * running in the VM (rather than dexopt), so that must be taken into
+ * account.
+ */
+ UninitInstanceMap* uninit_map = new UninitInstanceMap(new_instance_count);
+
+ size_t idx = 0;
+ if (IsInitMethod(method)) {
+ uninit_map->map_[idx++].addr_ = kUninitThisArgAddr;
+ }
+
+ /*
+ * Run through and find the new-instance instructions.
+ */
+ uint32_t addr = 0;
+ uint32_t insns_size = code_item->insns_size_;
+ const byte* ptr = reinterpret_cast<const byte*>(code_item->insns_);
const Instruction* inst = Instruction::At(ptr);
+ while (addr < insns_size) {
+ Instruction::Code opcode = inst->Opcode();
+ if (opcode == Instruction::NEW_INSTANCE) {
+ uninit_map->map_[idx++].addr_ = addr;
+ }
- size_t width = 0;
-
- while (width < insns_size) {
- insn_flags[width] |= inst->Size();
- width += inst->Size();
+ addr += inst->Size();
inst = inst->Next();
}
- if (width != insns_size) {
- LOG(ERROR) << "VFY: code did not end where expected (" << width << " vs. "
- << insns_size << ")";
+ assert(idx == new_instance_count);
+ return uninit_map;
+}
+
+bool DexVerifier::IsInitMethod(const Method* method) {
+ return (method->GetName()->Equals("<init>"));
+}
+
+Class* DexVerifier::LookupClassByDescriptor(const Method* method,
+ const char* descriptor, VerifyError* failure) {
+ /*
+ * The compiler occasionally puts references to nonexistent
+ * classes in signatures. For example, if you have a non-static
+ * inner class with no constructor, the compiler provides
+ * a private <init> for you. Constructing the class
+ * requires <init>(parent), but the outer class can't call
+ * that because the method is private. So the compiler
+ * generates a package-scope <init>(parent,bogus) method that
+ * just calls the regular <init> (the "bogus" part being necessary
+ * to distinguish the signature of the synthetic method).
+ * Treating the bogus class as an instance of java.lang.Object
+ * allows the verifier to process the class successfully.
+ */
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const ClassLoader* class_loader =
+ method->GetDeclaringClass()->GetClassLoader();
+ Class* klass = class_linker->FindClass(descriptor, class_loader);
+
+ if (klass == NULL) {
+ Thread::Current()->ClearException();
+ if (strchr(descriptor, '$') != NULL) {
+ LOG(INFO) << "VFY: unable to find class referenced in signature ("
+ << descriptor << ")";
+ } else {
+ LOG(ERROR) << "VFY: unable to find class referenced in signature ("
+ << descriptor << ")";
+ }
+
+ /* Check if the descriptor is an array. */
+ if (descriptor[0] == '[') {
+ /*
+ * There should never be a problem loading primitive arrays.
+ */
+ if (descriptor[1] != 'L' && descriptor[1] != '[') {
+ LOG(ERROR) << "VFY: invalid char in signature in '" << descriptor
+ << "'";
+ *failure = VERIFY_ERROR_GENERIC;
+ }
+
+ /*
+ * Try to continue with base array type. This will let
+ * us pass basic stuff (e.g. get array len) that wouldn't
+ * fly with an Object. This is NOT correct if the
+ * missing type is a primitive array, but we should never
+ * have a problem loading those. (I'm not convinced this
+ * is correct or even useful. Just use Object here?)
+ */
+ klass = class_linker->FindClass("[Ljava/lang/Object;", class_loader);
+ } else if (descriptor[0] == 'L') {
+ /*
+ * We are looking at a non-array reference descriptor;
+ * try to continue with base reference type.
+ */
+ klass = class_linker->FindSystemClass("Ljava/lang/Object;");
+ } else {
+ /* We are looking at a primitive type. */
+ LOG(ERROR) << "VFY: invalid char in signature in '" << descriptor << "'";
+ *failure = VERIFY_ERROR_GENERIC;
+ }
+
+ if (klass == NULL) {
+ *failure = VERIFY_ERROR_GENERIC;
+ }
+ }
+
+ if (klass->IsPrimitive()) {
+ LOG(ERROR) << "VFY: invalid use of primitive type '" << descriptor << "'";
+ *failure = VERIFY_ERROR_GENERIC;
+ klass = NULL;
+ }
+
+ return klass;
+}
+
+Class* DexVerifier::LookupSignatureClass(const Method* method, std::string sig,
+ VerifyError* failure) {
+ assert(sig[0] == 'L');
+ size_t end = sig.find(';');
+
+ if (end == std::string::npos) {
+ LOG(ERROR) << "VFY: bad signature component '" << sig << "' (missing ';')";
+ *failure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+
+ return LookupClassByDescriptor(method, sig.substr(0, end + 1).c_str(),
+ failure);
+}
+
+Class* DexVerifier::LookupSignatureArrayClass(const Method* method,
+ std::string sig, VerifyError* failure) {
+ assert(sig[0] == '[');
+ size_t end = 0;
+
+ while (sig[end] == '[')
+ end++;
+
+ if (sig[end] == 'L') {
+ end = sig.find(';');
+ if (end == std::string::npos) {
+ LOG(ERROR) << "VFY: bad signature component '" << sig
+ << "' (missing ';')";
+ *failure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+ }
+
+ return LookupClassByDescriptor(method, sig.substr(0, end + 1).c_str(),
+ failure);
+}
+
+bool DexVerifier::SetTypesFromSignature(VerifierData* vdata, RegType* reg_types)
+{
+ Method* method = vdata->method_;
+ const DexFile* dex_file = vdata->dex_file_;
+ const DexFile::CodeItem* code_item = vdata->code_item_;
+ UninitInstanceMap* uninit_map = vdata->uninit_map_;
+
+ int arg_start = code_item->registers_size_ - code_item->ins_size_;
+ int expected_args = code_item->ins_size_; /* long/double count as two */
+ int actual_args = 0;
+
+ assert(arg_start >= 0); /* should have been verified earlier */
+
+ /*
+ * Include the "this" pointer.
+ */
+ if (!method->IsStatic()) {
+ /*
+ * If this is a constructor for a class other than java.lang.Object,
+ * mark the first ("this") argument as uninitialized. This restricts
+ * field access until the superclass constructor is called.
+ */
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Class* klass_object = class_linker->FindSystemClass("Ljava/lang/Object;");
+ if (IsInitMethod(method) && method->GetDeclaringClass() != klass_object) {
+ int idx = SetUninitInstance(uninit_map, kUninitThisArgAddr,
+ method->GetDeclaringClass());
+ assert(idx == 0);
+ reg_types[arg_start + actual_args] = RegTypeFromUninitIndex(idx);
+ } else {
+ reg_types[arg_start + actual_args] =
+ RegTypeFromClass(method->GetDeclaringClass());
+ }
+ actual_args++;
+ }
+
+ const DexFile::ProtoId& proto_id =
+ dex_file->GetProtoId(method->GetProtoIdx());
+ DexFile::ParameterIterator iterator(*dex_file, proto_id);
+ VerifyError failure = VERIFY_ERROR_NONE;
+
+ for (; iterator.HasNext(); iterator.Next()) {
+ const char* descriptor = iterator.GetDescriptor();
+
+ if (descriptor == NULL) {
+ break;
+ }
+
+ if (actual_args >= expected_args) {
+ LOG(ERROR) << "VFY: expected " << expected_args << " args, found more ("
+ << descriptor << ")";
+ return false;
+ }
+
+ switch (*descriptor) {
+ case 'L':
+ case '[':
+ /*
+ * We assume that reference arguments are initialized. The
+ * only way it could be otherwise (assuming the caller was
+ * verified) is if the current method is <init>, but in that
+ * case it's effectively considered initialized the instant
+ * we reach here (in the sense that we can return without
+ * doing anything or call virtual methods).
+ */
+ {
+ Class* klass =
+ LookupClassByDescriptor(method, descriptor, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ return false;
+ reg_types[arg_start + actual_args] = RegTypeFromClass(klass);
+ }
+ actual_args++;
+ break;
+ case 'Z':
+ reg_types[arg_start + actual_args] = kRegTypeBoolean;
+ actual_args++;
+ break;
+ case 'C':
+ reg_types[arg_start + actual_args] = kRegTypeChar;
+ actual_args++;
+ break;
+ case 'B':
+ reg_types[arg_start + actual_args] = kRegTypeByte;
+ actual_args++;
+ break;
+ case 'I':
+ reg_types[arg_start + actual_args] = kRegTypeInteger;
+ actual_args++;
+ break;
+ case 'S':
+ reg_types[arg_start + actual_args] = kRegTypeShort;
+ actual_args++;
+ break;
+ case 'F':
+ reg_types[arg_start + actual_args] = kRegTypeFloat;
+ actual_args++;
+ break;
+ case 'D':
+ reg_types[arg_start + actual_args] = kRegTypeDoubleLo;
+ reg_types[arg_start + actual_args +1] = kRegTypeDoubleHi;
+ actual_args += 2;
+ break;
+ case 'J':
+ reg_types[arg_start + actual_args] = kRegTypeLongLo;
+ reg_types[arg_start + actual_args +1] = kRegTypeLongHi;
+ actual_args += 2;
+ break;
+ default:
+ LOG(ERROR) << "VFY: unexpected signature type char '" << descriptor
+ << "'";
+ return false;
+ }
+ }
+
+ if (actual_args != expected_args) {
+ LOG(ERROR) << "VFY: expected " << expected_args << " args, found "
+ << actual_args;
return false;
}
+ const char* descriptor = dex_file->GetReturnTypeDescriptor(proto_id);
+
+ /*
+ * Validate return type. We don't do the type lookup; just want to make
+ * sure that it has the right format. Only major difference from the
+ * method argument format is that 'V' is supported.
+ */
+ switch (*descriptor) {
+ case 'I':
+ case 'C':
+ case 'S':
+ case 'B':
+ case 'Z':
+ case 'V':
+ case 'F':
+ case 'D':
+ case 'J':
+ if (*(descriptor + 1) != '\0')
+ return false;
+ break;
+ case '[':
+ /* single/multi, object/primitive */
+ while (*++descriptor == '[')
+ ;
+ if (*descriptor == 'L') {
+ while (*++descriptor != ';' && *descriptor != '\0')
+ ;
+ if (*descriptor != ';')
+ return false;
+ } else {
+ if (*(descriptor+1) != '\0')
+ return false;
+ }
+ break;
+ case 'L':
+ /* could be more thorough here, but shouldn't be required */
+ while (*++descriptor != ';' && *descriptor != '\0')
+ ;
+ if (*descriptor != ';')
+ return false;
+ break;
+ default:
+ return false;
+ }
+
return true;
}
-/*
- * Set the "in try" flags for all instructions protected by "try" statements.
- * Also sets the "branch target" flags for exception handlers.
- *
- * Call this after widths have been set in "insn_flags".
- *
- * Returns "false" if something in the exception table looks fishy, but
- * we're expecting the exception table to be somewhat sane.
- */
-static bool ScanTryCatchBlocks(const DexFile::CodeItem* code_item,
- uint32_t insn_flags[]) {
+int DexVerifier::SetUninitInstance(UninitInstanceMap* uninit_map, int addr,
+ Class* klass) {
+ int idx;
+ assert(klass != NULL);
+
+ /* TODO: binary search when numEntries > 8 */
+ for (idx = uninit_map->num_entries_ - 1; idx >= 0; idx--) {
+ if (uninit_map->map_[idx].addr_ == addr) {
+ if (uninit_map->map_[idx].klass_ != NULL &&
+ uninit_map->map_[idx].klass_ != klass) {
+ LOG(ERROR) << "VFY: addr " << addr << " already set to "
+ << (int) uninit_map->map_[idx].klass_ << ", not setting to "
+ << (int) klass;
+ return -1; // already set to something else??
+ }
+ uninit_map->map_[idx].klass_ = klass;
+ return idx;
+ }
+ }
+
+ LOG(ERROR) << "VFY: addr " << addr << " not found in uninit map";
+ assert(false); // shouldn't happen
+ return -1;
+}
+
+bool DexVerifier::CodeFlowVerifyMethod(VerifierData* vdata,
+ RegisterTable* reg_table) {
+ const Method* method = vdata->method_;
+ const DexFile::CodeItem* code_item = vdata->code_item_;
+ InsnFlags* insn_flags = vdata->insn_flags_;
+ const uint16_t* insns = code_item->insns_;
uint32_t insns_size = code_item->insns_size_;
- uint32_t tries_size = code_item->tries_size_;
+ size_t insn_idx, start_guess;
- if (tries_size == 0) {
- return true;
- }
+ /* Begin by marking the first instruction as "changed". */
+ InsnSetChanged(insn_flags, 0, true);
- const DexFile::TryItem* tries = DexFile::dexGetTryItems(*code_item, 0);
+ start_guess = 0;
- for (uint32_t idx = 0; idx < tries_size; idx++) {
- const DexFile::TryItem* try_item = &tries[idx];
- uint32_t start = try_item->start_addr_;
- uint32_t end = start + try_item->insn_count_;
+ /* Continue until no instructions are marked "changed". */
+ while (true) {
+ /*
+ * Find the first marked one. Use "start_guess" as a way to find
+ * one quickly.
+ */
+ for (insn_idx = start_guess; insn_idx < insns_size; insn_idx++) {
+ if (InsnIsChanged(insn_flags, insn_idx))
+ break;
+ }
- if ((start >= end) || (start >= insns_size) || (end > insns_size)) {
- LOG(ERROR) << "VFY: bad exception entry: startAddr=" << start
- << " endAddr=" << end << " (size=" << insns_size << ")";
+ if (insn_idx == insns_size) {
+ if (start_guess != 0) {
+ /* try again, starting from the top */
+ start_guess = 0;
+ continue;
+ } else {
+ /* all flags are clear */
+ break;
+ }
+ }
+
+ /*
+ * We carry the working set of registers from instruction to
+ * instruction. If this address can be the target of a branch
+ * (or throw) instruction, or if we're skipping around chasing
+ * "changed" flags, we need to load the set of registers from
+ * the table.
+ *
+ * Because we always prefer to continue on to the next instruction,
+ * we should never have a situation where we have a stray
+ * "changed" flag set on an instruction that isn't a branch target.
+ */
+ if (InsnIsBranchTarget(insn_flags, insn_idx)) {
+ RegisterLine* work_line = ®_table->work_line_;
+ CopyLineFromTable(work_line, reg_table, insn_idx);
+ } else {
+#ifndef NDEBUG
+ /*
+ * Sanity check: retrieve the stored register line (assuming
+ * a full table) and make sure it actually matches.
+ */
+ RegisterLine* register_line = GetRegisterLine(reg_table, insn_idx);
+ if (register_line->reg_types_ != NULL && CompareLineToTable(reg_table,
+ insn_idx, ®_table->work_line_) != 0) {
+ Class* klass = method->GetDeclaringClass();
+ LOG(ERROR) << "HUH? work_line diverged in "
+ << klass->GetDescriptor()->ToModifiedUtf8() << "."
+ << method->GetName()->ToModifiedUtf8() << " "
+ << method->GetSignature()->ToModifiedUtf8();
+ }
+#endif
+ }
+
+ if (!CodeFlowVerifyInstruction(vdata, reg_table, insn_idx, &start_guess)) {
+ Class* klass = method->GetDeclaringClass();
+ LOG(ERROR) << "VFY: failure to verify "
+ << klass->GetDescriptor()->ToModifiedUtf8() << "."
+ << method->GetName()->ToModifiedUtf8() << " "
+ << method->GetSignature()->ToModifiedUtf8();
return false;
}
- if (InsnGetWidth(insn_flags, start) == 0) {
- LOG(ERROR) << "VFY: 'try' block starts inside an instruction ("
- << start << ")";
- return false;
- }
+ /* Clear "changed" and mark as visited. */
+ InsnSetVisited(insn_flags, insn_idx, true);
+ InsnSetChanged(insn_flags, insn_idx, false);
+ }
- uint32_t addr;
- for (addr = start; addr < end; addr += InsnGetWidth(insn_flags, addr)) {
- InsnSetInTry(insn_flags, addr, true);
+ if (DEAD_CODE_SCAN && ((method->GetAccessFlags() & kAccWritable) == 0)) {
+ /*
+ * Scan for dead code. There's nothing "evil" about dead code
+ * (besides the wasted space), but it indicates a flaw somewhere
+ * down the line, possibly in the verifier.
+ *
+ * If we've substituted "always throw" instructions into the stream,
+ * we are almost certainly going to have some dead code.
+ */
+ int dead_start = -1;
+ for (insn_idx = 0; insn_idx < insns_size;
+ insn_idx += InsnGetWidth(insn_flags, insn_idx)) {
+ /*
+ * Switch-statement data doesn't get "visited" by scanner. It
+ * may or may not be preceded by a padding NOP (for alignment).
+ */
+ if (insns[insn_idx] == Instruction::kPackedSwitchSignature ||
+ insns[insn_idx] == Instruction::kSparseSwitchSignature ||
+ insns[insn_idx] == Instruction::kArrayDataSignature ||
+ (insns[insn_idx] == Instruction::NOP &&
+ (insns[insn_idx + 1] == Instruction::kPackedSwitchSignature ||
+ insns[insn_idx + 1] == Instruction::kSparseSwitchSignature ||
+ insns[insn_idx + 1] == Instruction::kArrayDataSignature))) {
+ InsnSetVisited(insn_flags, insn_idx, true);
+ }
+
+ if (!InsnIsVisited(insn_flags, insn_idx)) {
+ if (dead_start < 0)
+ dead_start = insn_idx;
+ } else if (dead_start >= 0) {
+ Class* klass = method->GetDeclaringClass();
+ LOG(INFO) << "VFY: dead code 0x" << std::hex << dead_start << "-"
+ << insn_idx - 1 << std::dec << " in "
+ << klass->GetDescriptor()->ToModifiedUtf8() << "."
+ << method->GetName()->ToModifiedUtf8() << " "
+ << method->GetSignature()->ToModifiedUtf8();
+ dead_start = -1;
+ }
+ }
+ if (dead_start >= 0) {
+ Class* klass = method->GetDeclaringClass();
+ LOG(INFO) << "VFY: dead code 0x" << std::hex << dead_start << "-"
+ << insn_idx - 1 << std::dec << " in "
+ << klass->GetDescriptor()->ToModifiedUtf8() << "."
+ << method->GetName()->ToModifiedUtf8() << " "
+ << method->GetSignature()->ToModifiedUtf8();
}
}
- /* Iterate over each of the handlers to verify target addresses. */
+ return true;
+}
+
+bool DexVerifier::CodeFlowVerifyInstruction(VerifierData* vdata,
+ RegisterTable* reg_table, uint32_t insn_idx, size_t* start_guess) {
+ const Method* method = vdata->method_;
+ Class* klass = method->GetDeclaringClass();
+ const DexFile::CodeItem* code_item = vdata->code_item_;
+ InsnFlags* insn_flags = vdata->insn_flags_;
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ UninitInstanceMap* uninit_map = vdata->uninit_map_;
+ const uint16_t* insns = code_item->insns_ + insn_idx;
+ uint32_t insns_size = code_item->insns_size_;
+ uint32_t registers_size = code_item->registers_size_;
+
+#ifdef VERIFIER_STATS
+ if (InsnIsVisited(insn_flags, insn_idx)) {
+ gDvm.verifierStats.instrsReexamined++;
+ } else {
+ gDvm.verifierStats.instrsExamined++;
+ }
+#endif
+
+ /*
+ * Once we finish decoding the instruction, we need to figure out where
+ * we can go from here. There are three possible ways to transfer
+ * control to another statement:
+ *
+ * (1) Continue to the next instruction. Applies to all but
+ * unconditional branches, method returns, and exception throws.
+ * (2) Branch to one or more possible locations. Applies to branches
+ * and switch statements.
+ * (3) Exception handlers. Applies to any instruction that can
+ * throw an exception that is handled by an encompassing "try"
+ * block.
+ *
+ * We can also return, in which case there is no successor instruction
+ * from this point.
+ *
+ * The behavior can be determined from the OpcodeFlags.
+ */
+ RegisterLine* work_line = ®_table->work_line_;
+ const DexFile* dex_file = vdata->dex_file_;
+ const byte* ptr = reinterpret_cast<const byte*>(insns);
+ const Instruction* inst = Instruction::At(ptr);
+ Instruction::DecodedInstruction dec_insn(inst);
+ int opcode_flag = inst->Flag();
+
+ Class* res_class;
+ int32_t branch_target = 0;
+ RegType tmp_type;
+ bool just_set_result = false;
+ VerifyError failure = VERIFY_ERROR_NONE;
+
+ /*
+ * Make a copy of the previous register state. If the instruction
+ * can throw an exception, we will copy/merge this into the "catch"
+ * address rather than work_line, because we don't want the result
+ * from the "successful" code path (e.g. a check-cast that "improves"
+ * a type) to be visible to the exception handler.
+ */
+ if ((opcode_flag & Instruction::kThrow) != 0 &&
+ InsnIsInTry(insn_flags, insn_idx)) {
+ CopyRegisterLine(®_table->saved_line_, work_line,
+ reg_table->insn_reg_count_plus_);
+ } else {
+#ifndef NDEBUG
+ memset(reg_table->saved_line_.reg_types_, 0xdd,
+ reg_table->insn_reg_count_plus_ * sizeof(RegType));
+#endif
+ }
+
+ switch (dec_insn.opcode_) {
+ case Instruction::NOP:
+ /*
+ * A "pure" NOP has no effect on anything. Data tables start with
+ * a signature that looks like a NOP; if we see one of these in
+ * the course of executing code then we have a problem.
+ */
+ if (dec_insn.vA_ != 0) {
+ LOG(ERROR) << "VFY: encountered data table in instruction stream";
+ failure = VERIFY_ERROR_GENERIC;
+ }
+ break;
+
+ case Instruction::MOVE:
+ case Instruction::MOVE_FROM16:
+ case Instruction::MOVE_16:
+ CopyRegister1(work_line, dec_insn.vA_, dec_insn.vB_, kTypeCategory1nr,
+ &failure);
+ break;
+ case Instruction::MOVE_WIDE:
+ case Instruction::MOVE_WIDE_FROM16:
+ case Instruction::MOVE_WIDE_16:
+ CopyRegister2(work_line, dec_insn.vA_, dec_insn.vB_, &failure);
+ break;
+ case Instruction::MOVE_OBJECT:
+ case Instruction::MOVE_OBJECT_FROM16:
+ case Instruction::MOVE_OBJECT_16:
+ CopyRegister1(work_line, dec_insn.vA_, dec_insn.vB_, kTypeCategoryRef,
+ &failure);
+ break;
+
+ /*
+ * The move-result instructions copy data out of a "pseudo-register"
+ * with the results from the last method invocation. In practice we
+ * might want to hold the result in an actual CPU register, so the
+ * Dalvik spec requires that these only appear immediately after an
+ * invoke or filled-new-array.
+ *
+ * These calls invalidate the "result" register. (This is now
+ * redundant with the reset done below, but it can make the debug info
+ * easier to read in some cases.)
+ */
+ case Instruction::MOVE_RESULT:
+ CopyResultRegister1(work_line, registers_size, dec_insn.vA_,
+ kTypeCategory1nr, &failure);
+ break;
+ case Instruction::MOVE_RESULT_WIDE:
+ CopyResultRegister2(work_line, registers_size, dec_insn.vA_, &failure);
+ break;
+ case Instruction::MOVE_RESULT_OBJECT:
+ CopyResultRegister1(work_line, registers_size, dec_insn.vA_,
+ kTypeCategoryRef, &failure);
+ break;
+
+ case Instruction::MOVE_EXCEPTION:
+ /*
+ * This statement can only appear as the first instruction in an
+ * exception handler (though not all exception handlers need to
+ * have one of these). We verify that as part of extracting the
+ * exception type from the catch block list.
+ *
+ * "res_class" will hold the closest common superclass of all
+ * exceptions that can be handled here.
+ */
+ res_class = GetCaughtExceptionType(vdata, insn_idx, &failure);
+ if (res_class == NULL) {
+ assert(failure != VERIFY_ERROR_NONE);
+ } else {
+ SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(res_class));
+ }
+ break;
+
+ case Instruction::RETURN_VOID:
+ if (!CheckConstructorReturn(method, work_line, registers_size)) {
+ failure = VERIFY_ERROR_GENERIC;
+ } else if (GetMethodReturnType(dex_file, method) != kRegTypeUnknown) {
+ LOG(ERROR) << "VFY: return-void not expected";
+ failure = VERIFY_ERROR_GENERIC;
+ }
+ break;
+ case Instruction::RETURN:
+ if (!CheckConstructorReturn(method, work_line, registers_size)) {
+ failure = VERIFY_ERROR_GENERIC;
+ } else {
+ /* check the method signature */
+ RegType return_type = GetMethodReturnType(dex_file, method);
+ CheckTypeCategory(return_type, kTypeCategory1nr, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ LOG(ERROR) << "VFY: return-1nr not expected";
+
+ /*
+ * compiler may generate synthetic functions that write byte
+ * values into boolean fields. Also, it may use integer values
+ * for boolean, byte, short, and character return types.
+ */
+ RegType src_type = GetRegisterType(work_line, dec_insn.vA_);
+ if ((return_type == kRegTypeBoolean && src_type == kRegTypeByte) ||
+ ((return_type == kRegTypeBoolean || return_type == kRegTypeByte ||
+ return_type == kRegTypeShort || return_type == kRegTypeChar) &&
+ src_type == kRegTypeInteger))
+ return_type = src_type;
+
+ /* check the register contents */
+ VerifyRegisterType(work_line, dec_insn.vA_, return_type, &failure);
+ if (failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: return-1nr on invalid register v" << dec_insn.vA_;
+ }
+ }
+ break;
+ case Instruction::RETURN_WIDE:
+ if (!CheckConstructorReturn(method, work_line, registers_size)) {
+ failure = VERIFY_ERROR_GENERIC;
+ } else {
+ RegType return_type;
+
+ /* check the method signature */
+ return_type = GetMethodReturnType(dex_file, method);
+ CheckTypeCategory(return_type, kTypeCategory2, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ LOG(ERROR) << "VFY: return-wide not expected";
+
+ /* check the register contents */
+ VerifyRegisterType(work_line, dec_insn.vA_, return_type, &failure);
+ if (failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: return-wide on invalid register pair v"
+ << dec_insn.vA_;
+ }
+ }
+ break;
+ case Instruction::RETURN_OBJECT:
+ if (!CheckConstructorReturn(method, work_line, registers_size)) {
+ failure = VERIFY_ERROR_GENERIC;
+ } else {
+ RegType return_type = GetMethodReturnType(dex_file, method);
+ CheckTypeCategory(return_type, kTypeCategoryRef, &failure);
+ if (failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: return-object not expected";
+ break;
+ }
+
+ /* return_type is the *expected* return type, not register value */
+ assert(return_type != kRegTypeZero);
+ assert(!RegTypeIsUninitReference(return_type));
+
+ /*
+ * Verify that the reference in vAA is an instance of the type
+ * in "return_type". The Zero type is allowed here. If the
+ * method is declared to return an interface, then any
+ * initialized reference is acceptable.
+ *
+ * Note GetClassFromRegister fails if the register holds an
+ * uninitialized reference, so we do not allow them to be
+ * returned.
+ */
+ Class* decl_class = RegTypeInitializedReferenceToClass(return_type);
+ res_class = GetClassFromRegister(work_line, dec_insn.vA_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ if (res_class != NULL) {
+ if (!decl_class->IsInterface() &&
+ //!res_class->InstanceOf(decl_class)) {
+ !decl_class->IsAssignableFrom(res_class)) {
+ LOG(ERROR) << "VFY: returning " << std::hex
+ << res_class->GetDescriptor()->ToModifiedUtf8()
+ << " (cl=0x" << (int) res_class->GetClassLoader()
+ << "), declared "
+ << decl_class->GetDescriptor()->ToModifiedUtf8()
+ << " (cl=0x" << (int) decl_class->GetClassLoader()
+ << ")" << std::dec;
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ }
+ break;
+
+ case Instruction::CONST_4:
+ case Instruction::CONST_16:
+ case Instruction::CONST:
+ /* could be boolean, int, float, or a null reference */
+ SetRegisterType(work_line, dec_insn.vA_,
+ DetermineCat1Const((int32_t) dec_insn.vB_));
+ break;
+ case Instruction::CONST_HIGH16:
+ /* could be boolean, int, float, or a null reference */
+ SetRegisterType(work_line, dec_insn.vA_,
+ DetermineCat1Const((int32_t) dec_insn.vB_ << 16));
+ break;
+ case Instruction::CONST_WIDE_16:
+ case Instruction::CONST_WIDE_32:
+ case Instruction::CONST_WIDE:
+ case Instruction::CONST_WIDE_HIGH16:
+ /* could be long or double; resolved upon use */
+ SetRegisterType(work_line, dec_insn.vA_, kRegTypeConstLo);
+ break;
+ case Instruction::CONST_STRING:
+ case Instruction::CONST_STRING_JUMBO:
+ SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(
+ class_linker->FindSystemClass("Ljava/lang/String;")));
+ break;
+ case Instruction::CONST_CLASS:
+ /* make sure we can resolve the class; access check is important */
+ res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+ if (res_class == NULL) {
+ const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
+ LOG(ERROR) << "VFY: unable to resolve const-class " << dec_insn.vB_
+ << " (" << bad_class_desc << ") in "
+ << klass->GetDescriptor()->ToModifiedUtf8();
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else {
+ SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(
+ class_linker->FindSystemClass("Ljava/lang/Class;")));
+ }
+ break;
+
+ case Instruction::MONITOR_ENTER:
+ HandleMonitorEnter(work_line, dec_insn.vA_, insn_idx, &failure);
+ break;
+ case Instruction::MONITOR_EXIT:
+ /*
+ * monitor-exit instructions are odd. They can throw exceptions,
+ * but when they do they act as if they succeeded and the PC is
+ * pointing to the following instruction. (This behavior goes back
+ * to the need to handle asynchronous exceptions, a now-deprecated
+ * feature that Dalvik doesn't support.)
+ *
+ * In practice we don't need to worry about this. The only
+ * exceptions that can be thrown from monitor-exit are for a
+ * null reference and -exit without a matching -enter. If the
+ * structured locking checks are working, the former would have
+ * failed on the -enter instruction, and the latter is impossible.
+ *
+ * This is fortunate, because issue 3221411 prevents us from
+ * chasing the "can throw" path when monitor verification is
+ * enabled. If we can fully verify the locking we can ignore
+ * some catch blocks (which will show up as "dead" code when
+ * we skip them here); if we can't, then the code path could be
+ * "live" so we still need to check it.
+ */
+ if (work_line->monitor_entries_ != NULL)
+ opcode_flag &= ~Instruction::kThrow;
+ HandleMonitorExit(work_line, dec_insn.vA_, insn_idx, &failure);
+ break;
+
+ case Instruction::CHECK_CAST:
+ /*
+ * If this instruction succeeds, we will promote register vA to
+ * the type in vB. (This could be a demotion -- not expected, so
+ * we don't try to address it.)
+ *
+ * If it fails, an exception is thrown, which we deal with later
+ * by ignoring the update to dec_insn.vA_ when branching to a handler.
+ */
+ res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+ if (res_class == NULL) {
+ const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
+ LOG(ERROR) << "VFY: unable to resolve check-cast " << dec_insn.vB_
+ << " (" << bad_class_desc << ") in "
+ << klass->GetDescriptor()->ToModifiedUtf8();
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else {
+ RegType orig_type;
+
+ orig_type = GetRegisterType(work_line, dec_insn.vA_);
+ if (!RegTypeIsReference(orig_type)) {
+ LOG(ERROR) << "VFY: check-cast on non-reference in v" << dec_insn.vA_;
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(res_class));
+ }
+ break;
+ case Instruction::INSTANCE_OF:
+ /* make sure we're checking a reference type */
+ tmp_type = GetRegisterType(work_line, dec_insn.vB_);
+ if (!RegTypeIsReference(tmp_type)) {
+ LOG(ERROR) << "VFY: vB not a reference (" << tmp_type << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /* make sure we can resolve the class; access check is important */
+ res_class = class_linker->ResolveType(*dex_file, dec_insn.vC_, klass);
+ if (res_class == NULL) {
+ const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vC_);
+ LOG(ERROR) << "VFY: unable to resolve instanceof " << dec_insn.vC_
+ << " (" << bad_class_desc << ") in "
+ << klass->GetDescriptor()->ToModifiedUtf8();
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else {
+ /* result is boolean */
+ SetRegisterType(work_line, dec_insn.vA_, kRegTypeBoolean);
+ }
+ break;
+
+ case Instruction::ARRAY_LENGTH:
+ res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ if (res_class != NULL && !res_class->IsArrayClass()) {
+ LOG(ERROR) << "VFY: array-length on non-array";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ SetRegisterType(work_line, dec_insn.vA_, kRegTypeInteger);
+ break;
+
+ case Instruction::NEW_INSTANCE:
+ res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+ if (res_class == NULL) {
+ const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
+ LOG(ERROR) << "VFY: unable to resolve new-instance " << dec_insn.vB_
+ << " (" << bad_class_desc << ") in "
+ << klass->GetDescriptor()->ToModifiedUtf8();
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else {
+ RegType uninit_type;
+
+ /* can't create an instance of an interface or abstract class */
+ if (res_class->IsAbstract() || res_class->IsInterface()) {
+ LOG(ERROR) << "VFY: new-instance on interface or abstract class"
+ << res_class->GetDescriptor()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_INSTANTIATION;
+ break;
+ }
+
+ /* add resolved class to uninit map if not already there */
+ int uidx = SetUninitInstance(uninit_map, insn_idx, res_class);
+ assert(uidx >= 0);
+ uninit_type = RegTypeFromUninitIndex(uidx);
+
+ /*
+ * Any registers holding previous allocations from this address
+ * that have not yet been initialized must be marked invalid.
+ */
+ MarkUninitRefsAsInvalid(work_line, registers_size, uninit_map,
+ uninit_type);
+
+ /* add the new uninitialized reference to the register ste */
+ SetRegisterType(work_line, dec_insn.vA_, uninit_type);
+ }
+ break;
+ case Instruction::NEW_ARRAY:
+ res_class = class_linker->ResolveType(*dex_file, dec_insn.vC_, klass);
+ if (res_class == NULL) {
+ const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vC_);
+ LOG(ERROR) << "VFY: unable to resolve new-array " << dec_insn.vC_
+ << " (" << bad_class_desc << ") in "
+ << klass->GetDescriptor()->ToModifiedUtf8();
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else if (!res_class->IsArrayClass()) {
+ LOG(ERROR) << "VFY: new-array on non-array class";
+ failure = VERIFY_ERROR_GENERIC;
+ } else {
+ /* make sure "size" register is valid type */
+ VerifyRegisterType(work_line, dec_insn.vB_, kRegTypeInteger, &failure);
+ /* set register type to array class */
+ SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(res_class));
+ }
+ break;
+ case Instruction::FILLED_NEW_ARRAY:
+ case Instruction::FILLED_NEW_ARRAY_RANGE:
+ res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+ if (res_class == NULL) {
+ const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
+ LOG(ERROR) << "VFY: unable to resolve filled-array " << dec_insn.vB_
+ << " (" << bad_class_desc << ") in "
+ << klass->GetDescriptor()->ToModifiedUtf8();
+ assert(failure != VERIFY_ERROR_GENERIC);
+ } else if (!res_class->IsArrayClass()) {
+ LOG(ERROR) << "VFY: filled-new-array on non-array class";
+ failure = VERIFY_ERROR_GENERIC;
+ } else {
+ bool is_range = (dec_insn.opcode_ ==
+ Instruction::FILLED_NEW_ARRAY_RANGE);
+
+ /* check the arguments to the instruction */
+ VerifyFilledNewArrayRegs(method, work_line, &dec_insn, res_class,
+ is_range, &failure);
+ /* filled-array result goes into "result" register */
+ SetResultRegisterType(work_line, registers_size,
+ RegTypeFromClass(res_class));
+ just_set_result = true;
+ }
+ break;
+
+ case Instruction::CMPL_FLOAT:
+ case Instruction::CMPG_FLOAT:
+ VerifyRegisterType(work_line, dec_insn.vB_, kRegTypeFloat, &failure);
+ VerifyRegisterType(work_line, dec_insn.vC_, kRegTypeFloat, &failure);
+ SetRegisterType(work_line, dec_insn.vA_, kRegTypeBoolean);
+ break;
+ case Instruction::CMPL_DOUBLE:
+ case Instruction::CMPG_DOUBLE:
+ VerifyRegisterType(work_line, dec_insn.vB_, kRegTypeDoubleLo, &failure);
+ VerifyRegisterType(work_line, dec_insn.vC_, kRegTypeDoubleLo, &failure);
+ SetRegisterType(work_line, dec_insn.vA_, kRegTypeBoolean);
+ break;
+ case Instruction::CMP_LONG:
+ VerifyRegisterType(work_line, dec_insn.vB_, kRegTypeLongLo, &failure);
+ VerifyRegisterType(work_line, dec_insn.vC_, kRegTypeLongLo, &failure);
+ SetRegisterType(work_line, dec_insn.vA_, kRegTypeBoolean);
+ break;
+
+ case Instruction::THROW:
+ res_class = GetClassFromRegister(work_line, dec_insn.vA_, &failure);
+ if (failure == VERIFY_ERROR_NONE && res_class != NULL) {
+ Class* throwable_class =
+ class_linker->FindSystemClass("Ljava/lang/Throwable;");
+ if (!throwable_class->IsAssignableFrom(res_class)) {
+ LOG(ERROR) << "VFY: thrown class "
+ << res_class->GetDescriptor()->ToModifiedUtf8()
+ << " not instanceof Throwable",
+ failure = VERIFY_ERROR_GENERIC;
+ }
+ }
+ break;
+
+ case Instruction::GOTO:
+ case Instruction::GOTO_16:
+ case Instruction::GOTO_32:
+ /* no effect on or use of registers */
+ break;
+
+ case Instruction::PACKED_SWITCH:
+ case Instruction::SPARSE_SWITCH:
+ /* verify that vAA is an integer, or can be converted to one */
+ VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeInteger, &failure);
+ break;
+
+ case Instruction::FILL_ARRAY_DATA:
+ {
+ RegType value_type;
+ const uint16_t *array_data;
+ uint16_t elem_width;
+
+ /* Similar to the verification done for APUT */
+ res_class = GetClassFromRegister(work_line, dec_insn.vA_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /* res_class can be null if the reg type is Zero */
+ if (res_class == NULL)
+ break;
+
+ Class::PrimitiveType prim_type =
+ res_class->GetComponentType()->GetPrimitiveType();
+ if (!res_class->IsArrayClass() || res_class->GetArrayRank() != 1 ||
+ prim_type == Class::kPrimNot || prim_type == Class::kPrimVoid) {
+ LOG(ERROR) << "VFY: invalid fill-array-data on " <<
+ res_class->GetDescriptor()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ value_type = PrimitiveTypeToRegType(prim_type);
+ assert(value_type != kRegTypeUnknown);
+
+ /*
+ * Now verify if the element width in the table matches the element
+ * width declared in the array
+ */
+ array_data = insns + (insns[1] | (((int32_t) insns[2]) << 16));
+ if (array_data[0] != Instruction::kArrayDataSignature) {
+ LOG(ERROR) << "VFY: invalid magic for array-data";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ switch (prim_type) {
+ case Class::kPrimBoolean:
+ case Class::kPrimByte:
+ elem_width = 1;
+ break;
+ case Class::kPrimChar:
+ case Class::kPrimShort:
+ elem_width = 2;
+ break;
+ case Class::kPrimFloat:
+ case Class::kPrimInt:
+ elem_width = 4;
+ break;
+ case Class::kPrimDouble:
+ case Class::kPrimLong:
+ elem_width = 8;
+ break;
+ default:
+ elem_width = 0;
+ break;
+ }
+
+ /*
+ * Since we don't compress the data in Dex, expect to see equal
+ * width of data stored in the table and expected from the array
+ * class.
+ */
+ if (array_data[1] != elem_width) {
+ LOG(ERROR) << "VFY: array-data size mismatch (" << array_data[1]
+ << " vs " << elem_width << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ }
+ }
+ break;
+
+ case Instruction::IF_EQ:
+ case Instruction::IF_NE:
+ {
+ RegType type1, type2;
+
+ type1 = GetRegisterType(work_line, dec_insn.vA_);
+ type2 = GetRegisterType(work_line, dec_insn.vB_);
+
+ /* both references? */
+ if (RegTypeIsReference(type1) && RegTypeIsReference(type2))
+ break;
+
+ /* both category-1nr? */
+ CheckTypeCategory(type1, kTypeCategory1nr, &failure);
+ CheckTypeCategory(type2, kTypeCategory1nr, &failure);
+ if (failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: args to if-eq/if-ne must both be refs or cat1";
+ break;
+ }
+ }
+ break;
+ case Instruction::IF_LT:
+ case Instruction::IF_GE:
+ case Instruction::IF_GT:
+ case Instruction::IF_LE:
+ tmp_type = GetRegisterType(work_line, dec_insn.vA_);
+ CheckTypeCategory(tmp_type, kTypeCategory1nr, &failure);
+ if (failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: args to 'if' must be cat-1nr";
+ break;
+ }
+ tmp_type = GetRegisterType(work_line, dec_insn.vB_);
+ CheckTypeCategory(tmp_type, kTypeCategory1nr, &failure);
+ if (failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: args to 'if' must be cat-1nr";
+ break;
+ }
+ break;
+ case Instruction::IF_EQZ:
+ case Instruction::IF_NEZ:
+ tmp_type = GetRegisterType(work_line, dec_insn.vA_);
+ if (RegTypeIsReference(tmp_type))
+ break;
+ CheckTypeCategory(tmp_type, kTypeCategory1nr, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ LOG(ERROR) << "VFY: expected cat-1 arg to if";
+ break;
+ case Instruction::IF_LTZ:
+ case Instruction::IF_GEZ:
+ case Instruction::IF_GTZ:
+ case Instruction::IF_LEZ:
+ tmp_type = GetRegisterType(work_line, dec_insn.vA_);
+ CheckTypeCategory(tmp_type, kTypeCategory1nr, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ LOG(ERROR) << "VFY: expected cat-1 arg to if";
+ break;
+
+ case Instruction::AGET:
+ tmp_type = kRegTypeConstInteger;
+ goto aget_1nr_common;
+ case Instruction::AGET_BOOLEAN:
+ tmp_type = kRegTypeBoolean;
+ goto aget_1nr_common;
+ case Instruction::AGET_BYTE:
+ tmp_type = kRegTypeByte;
+ goto aget_1nr_common;
+ case Instruction::AGET_CHAR:
+ tmp_type = kRegTypeChar;
+ goto aget_1nr_common;
+ case Instruction::AGET_SHORT:
+ tmp_type = kRegTypeShort;
+ goto aget_1nr_common;
+aget_1nr_common:
+ {
+ RegType src_type, index_type;
+
+ index_type = GetRegisterType(work_line, dec_insn.vC_);
+ CheckArrayIndexType(method, index_type, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ if (res_class != NULL) {
+ /* verify the class */
+ Class::PrimitiveType prim_type =
+ res_class->GetComponentType()->GetPrimitiveType();
+ if (!res_class->IsArrayClass() || res_class->GetArrayRank() != 1 ||
+ prim_type == Class::kPrimNot) {
+ LOG(ERROR) << "VFY: invalid aget-1nr target "
+ << res_class->GetDescriptor()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /* make sure array type matches instruction */
+ src_type = PrimitiveTypeToRegType(prim_type);
+
+ /* differentiate between float and int */
+ if (src_type == kRegTypeFloat || src_type == kRegTypeInteger)
+ tmp_type = src_type;
+
+ if (tmp_type != src_type) {
+ LOG(ERROR) << "VFY: invalid aget-1nr, array type=" << src_type
+ << " with inst type=" << tmp_type << " (on "
+ << res_class->GetDescriptor()->ToModifiedUtf8() << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ }
+ SetRegisterType(work_line, dec_insn.vA_, tmp_type);
+ }
+ break;
+
+ case Instruction::AGET_WIDE:
+ {
+ RegType dst_type, index_type;
+
+ index_type = GetRegisterType(work_line, dec_insn.vC_);
+ CheckArrayIndexType(method, index_type, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ if (res_class != NULL) {
+ /* verify the class */
+ Class::PrimitiveType prim_type =
+ res_class->GetComponentType()->GetPrimitiveType();
+ if (!res_class->IsArrayClass() || res_class->GetArrayRank() != 1 ||
+ prim_type == Class::kPrimNot) {
+ LOG(ERROR) << "VFY: invalid aget-wide target "
+ << res_class->GetDescriptor()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /* try to refine "dst_type" */
+ switch (prim_type) {
+ case Class::kPrimLong:
+ dst_type = kRegTypeLongLo;
+ break;
+ case Class::kPrimDouble:
+ dst_type = kRegTypeDoubleLo;
+ break;
+ default:
+ LOG(ERROR) << "VFY: invalid aget-wide on "
+ << res_class->GetDescriptor()->ToModifiedUtf8();
+ dst_type = kRegTypeUnknown;
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ } else {
+ /*
+ * Null array ref; this code path will fail at runtime. We
+ * know this is either long or double, so label it const.
+ */
+ dst_type = kRegTypeConstLo;
+ }
+ SetRegisterType(work_line, dec_insn.vA_, dst_type);
+ }
+ break;
+
+ case Instruction::AGET_OBJECT:
+ {
+ RegType dst_type, index_type;
+
+ index_type = GetRegisterType(work_line, dec_insn.vC_);
+ CheckArrayIndexType(method, index_type, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /* get the class of the array we're pulling an object from */
+ res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ if (res_class != NULL) {
+ Class* element_class;
+
+ assert(res_class != NULL);
+ if (!res_class->IsArrayClass()) {
+ LOG(ERROR) << "VFY: aget-object on non-array class";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ assert(res_class->GetComponentType() != NULL);
+
+ /*
+ * Find the element class. res_class->GetComponentType() indicates
+ * the basic type, which won't be what we want for a
+ * multi-dimensional array.
+ */
+ if (res_class->GetDescriptor()->CharAt(1) == '[') {
+ assert(res_class->GetArrayRank() > 1);
+ std::string descriptor =
+ res_class->GetDescriptor()->ToModifiedUtf8();
+ element_class = class_linker->FindClass(descriptor.c_str() + 1,
+ res_class->GetClassLoader());
+ } else if (res_class->GetDescriptor()->CharAt(1) == 'L') {
+ assert(res_class->GetArrayRank() == 1);
+ element_class = res_class->GetComponentType();
+ } else {
+ LOG(ERROR) << "VFY: aget-object on non-ref array class ("
+ << res_class->GetDescriptor()->ToModifiedUtf8() << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ dst_type = RegTypeFromClass(element_class);
+ } else {
+ /*
+ * The array reference is NULL, so the current code path will
+ * throw an exception. For proper merging with later code
+ * paths, and correct handling of "if-eqz" tests on the
+ * result of the array get, we want to treat this as a null
+ * reference.
+ */
+ dst_type = kRegTypeZero;
+ }
+ SetRegisterType(work_line, dec_insn.vA_, dst_type);
+ }
+ break;
+ case Instruction::APUT:
+ tmp_type = kRegTypeInteger;
+ goto aput_1nr_common;
+ case Instruction::APUT_BOOLEAN:
+ tmp_type = kRegTypeBoolean;
+ goto aput_1nr_common;
+ case Instruction::APUT_BYTE:
+ tmp_type = kRegTypeByte;
+ goto aput_1nr_common;
+ case Instruction::APUT_CHAR:
+ tmp_type = kRegTypeChar;
+ goto aput_1nr_common;
+ case Instruction::APUT_SHORT:
+ tmp_type = kRegTypeShort;
+ goto aput_1nr_common;
+aput_1nr_common:
+ {
+ RegType src_type, dst_type, index_type;
+
+ index_type = GetRegisterType(work_line, dec_insn.vC_);
+ CheckArrayIndexType(method, index_type, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /* res_class can be null if the reg type is Zero */
+ if (res_class == NULL)
+ break;
+
+ Class::PrimitiveType prim_type =
+ res_class->GetComponentType()->GetPrimitiveType();
+ if (!res_class->IsArrayClass() || res_class->GetArrayRank() != 1 ||
+ prim_type == Class::kPrimNot) {
+ LOG(ERROR) << "VFY: invalid aput-1nr on "
+ << res_class->GetDescriptor()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /* verify that instruction matches array */
+ dst_type = PrimitiveTypeToRegType(prim_type);
+
+ /* correct if float */
+ if (dst_type == kRegTypeFloat)
+ tmp_type = kRegTypeFloat;
+
+ /* make sure the source register has the correct type */
+ src_type = GetRegisterType(work_line, dec_insn.vA_);
+ if (!CanConvertTo1nr(src_type, tmp_type)) {
+ LOG(ERROR) << "VFY: invalid reg type " << src_type
+ << " on aput instr (need " << tmp_type << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ VerifyRegisterType(work_line, dec_insn.vA_, dst_type, &failure);
+
+ if (failure != VERIFY_ERROR_NONE || dst_type == kRegTypeUnknown ||
+ tmp_type != dst_type) {
+ LOG(ERROR) << "VFY: invalid aput-1nr on "
+ << res_class->GetDescriptor()->ToModifiedUtf8()
+ << " (inst=" << tmp_type << " dst=" << dst_type << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ break;
+ case Instruction::APUT_WIDE:
+ tmp_type = GetRegisterType(work_line, dec_insn.vC_);
+ CheckArrayIndexType(method, tmp_type, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ if (res_class != NULL) {
+ Class::PrimitiveType prim_type =
+ res_class->GetComponentType()->GetPrimitiveType();
+ /* verify the class and try to refine "dst_type" */
+ if (!res_class->IsArrayClass() || res_class->GetArrayRank() != 1 ||
+ prim_type == Class::kPrimNot)
+ {
+ LOG(ERROR) << "VFY: invalid aput-wide on "
+ << res_class->GetDescriptor()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ switch (prim_type) {
+ case Class::kPrimLong:
+ VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeLongLo,
+ &failure);
+ break;
+ case Class::kPrimDouble:
+ VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeDoubleLo,
+ &failure);
+ break;
+ default:
+ LOG(ERROR) << "VFY: invalid aput-wide on "
+ << res_class->GetDescriptor()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ break;
+ case Instruction::APUT_OBJECT:
+ tmp_type = GetRegisterType(work_line, dec_insn.vC_);
+ CheckArrayIndexType(method, tmp_type, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /* get the ref we're storing; Zero is okay, Uninit is not */
+ res_class = GetClassFromRegister(work_line, dec_insn.vA_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ if (res_class != NULL) {
+ Class* array_class;
+ Class* element_class;
+
+ /*
+ * Get the array class. If the array ref is null, we won't
+ * have type information (and we'll crash at runtime with a
+ * null pointer exception).
+ */
+ array_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+
+ if (array_class != NULL) {
+ /* see if the array holds a compatible type */
+ if (!array_class->IsArrayClass()) {
+ LOG(ERROR) << "VFY: invalid aput-object on "
+ << array_class->GetDescriptor()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /*
+ * Find the element class. res_class->GetComponentType() indicates
+ * the basic type, which won't be what we want for a
+ * multi-dimensional array.
+ *
+ * All we want to check here is that the element type is a
+ * reference class. We *don't* check instanceof here, because
+ * you can still put a String into a String[] after the latter
+ * has been cast to an Object[].
+ */
+ if (array_class->GetDescriptor()->CharAt(1) == '[') {
+ assert(array_class->GetArrayRank() > 1);
+ std::string descriptor =
+ array_class->GetDescriptor()->ToModifiedUtf8();
+ element_class = class_linker->FindClass(descriptor.c_str() + 1,
+ array_class->GetClassLoader());
+ } else {
+ assert(array_class->GetArrayRank() == 1);
+ element_class = array_class->GetComponentType();
+ }
+ if (element_class->GetPrimitiveType() != Class::kPrimNot) {
+ LOG(ERROR) << "VFY: invalid aput-object of "
+ << res_class->GetDescriptor()->ToModifiedUtf8()
+ << " into "
+ << array_class->GetDescriptor()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ }
+ break;
+
+ case Instruction::IGET:
+ tmp_type = kRegTypeInteger;
+ goto iget_1nr_common;
+ case Instruction::IGET_BOOLEAN:
+ tmp_type = kRegTypeBoolean;
+ goto iget_1nr_common;
+ case Instruction::IGET_BYTE:
+ tmp_type = kRegTypeByte;
+ goto iget_1nr_common;
+ case Instruction::IGET_CHAR:
+ tmp_type = kRegTypeChar;
+ goto iget_1nr_common;
+ case Instruction::IGET_SHORT:
+ tmp_type = kRegTypeShort;
+ goto iget_1nr_common;
+iget_1nr_common:
+ {
+ Field* inst_field;
+ RegType obj_type, field_type;
+
+ obj_type = GetRegisterType(work_line, dec_insn.vB_);
+ inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /* make sure the field's type is compatible with expectation */
+ field_type =
+ PrimitiveTypeToRegType(inst_field->GetType()->GetPrimitiveType());
+
+ /* correct if float */
+ if (field_type == kRegTypeFloat)
+ tmp_type = kRegTypeFloat;
+
+ if (field_type == kRegTypeUnknown || tmp_type != field_type) {
+ Class* inst_field_class = inst_field->GetDeclaringClass();
+ LOG(ERROR) << "VFY: invalid iget-1nr of "
+ << inst_field_class->GetDescriptor()->ToModifiedUtf8()
+ << "." << inst_field->GetName()->ToModifiedUtf8()
+ << " (inst=" << tmp_type << " field=" << field_type << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ SetRegisterType(work_line, dec_insn.vA_, tmp_type);
+ }
+ break;
+ case Instruction::IGET_WIDE:
+ {
+ RegType dst_type;
+ Field* inst_field;
+ RegType obj_type;
+
+ obj_type = GetRegisterType(work_line, dec_insn.vB_);
+ inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+ Class* inst_field_class = inst_field->GetDeclaringClass();
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ /* check the type, which should be prim */
+ switch (inst_field->GetType()->GetPrimitiveType()) {
+ case Class::kPrimDouble:
+ dst_type = kRegTypeDoubleLo;
+ break;
+ case Class::kPrimLong:
+ dst_type = kRegTypeLongLo;
+ break;
+ default:
+ LOG(ERROR) << "VFY: invalid iget-wide of "
+ << inst_field_class->GetDescriptor()->ToModifiedUtf8()
+ << "." << inst_field->GetName()->ToModifiedUtf8();
+ dst_type = kRegTypeUnknown;
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (failure == VERIFY_ERROR_NONE) {
+ SetRegisterType(work_line, dec_insn.vA_, dst_type);
+ }
+ }
+ break;
+ case Instruction::IGET_OBJECT:
+ {
+ Class* field_class;
+ Field* inst_field;
+ RegType obj_type;
+
+ obj_type = GetRegisterType(work_line, dec_insn.vB_);
+ inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ field_class = inst_field->GetType();
+ if (field_class == NULL) {
+ /* class not found or primitive type */
+ LOG(ERROR) << "VFY: unable to recover field class from "
+ << inst_field->GetName()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (failure == VERIFY_ERROR_NONE) {
+ assert(!field_class->IsPrimitive());
+ SetRegisterType(work_line, dec_insn.vA_,
+ RegTypeFromClass(field_class));
+ }
+ }
+ break;
+ case Instruction::IPUT:
+ tmp_type = kRegTypeInteger;
+ goto iput_1nr_common;
+ case Instruction::IPUT_BOOLEAN:
+ tmp_type = kRegTypeBoolean;
+ goto iput_1nr_common;
+ case Instruction::IPUT_BYTE:
+ tmp_type = kRegTypeByte;
+ goto iput_1nr_common;
+ case Instruction::IPUT_CHAR:
+ tmp_type = kRegTypeChar;
+ goto iput_1nr_common;
+ case Instruction::IPUT_SHORT:
+ tmp_type = kRegTypeShort;
+ goto iput_1nr_common;
+iput_1nr_common:
+ {
+ RegType src_type, field_type, obj_type;
+ Field* inst_field;
+
+ obj_type = GetRegisterType(work_line, dec_insn.vB_);
+ inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ CheckFinalFieldAccess(method, inst_field, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /* get type of field we're storing into */
+ field_type =
+ PrimitiveTypeToRegType(inst_field->GetType()->GetPrimitiveType());
+ src_type = GetRegisterType(work_line, dec_insn.vA_);
+
+ /* correct if float */
+ if (field_type == kRegTypeFloat)
+ tmp_type = kRegTypeFloat;
+
+ /*
+ * compiler can generate synthetic functions that write byte values
+ * into boolean fields.
+ */
+ if (tmp_type == kRegTypeBoolean && src_type == kRegTypeByte)
+ tmp_type = kRegTypeByte;
+ if (field_type == kRegTypeBoolean && src_type == kRegTypeByte)
+ field_type = kRegTypeByte;
+
+ /* make sure the source register has the correct type */
+ if (!CanConvertTo1nr(src_type, tmp_type)) {
+ LOG(ERROR) << "VFY: invalid reg type " << src_type
+ << " on iput instr (need " << tmp_type << ")",
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ VerifyRegisterType(work_line, dec_insn.vA_, field_type, &failure);
+
+ if (failure != VERIFY_ERROR_NONE || field_type == kRegTypeUnknown ||
+ tmp_type != field_type) {
+ Class* inst_field_class = inst_field->GetDeclaringClass();
+ LOG(ERROR) << "VFY: invalid iput-1nr of "
+ << inst_field_class->GetDescriptor()->ToModifiedUtf8()
+ << "." << inst_field->GetName()->ToModifiedUtf8()
+ << " (inst=" << tmp_type << " field=" << field_type << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ break;
+ case Instruction::IPUT_WIDE:
+ Field* inst_field;
+ RegType obj_type;
+
+ obj_type = GetRegisterType(work_line, dec_insn.vB_);
+ inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ CheckFinalFieldAccess(method, inst_field, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /* check the type, which should be prim */
+ switch (inst_field->GetType()->GetPrimitiveType()) {
+ case Class::kPrimDouble:
+ VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeDoubleLo,
+ &failure);
+ break;
+ case Class::kPrimLong:
+ VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeLongLo, &failure);
+ break;
+ default:
+ LOG(ERROR) << "VFY: invalid iput-wide of "
+ << inst_field->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+ << "." << inst_field->GetName()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ break;
+ case Instruction::IPUT_OBJECT:
+ {
+ Class* field_class;
+ Class* value_class;
+ Field* inst_field;
+ RegType obj_type, value_type;
+
+ obj_type = GetRegisterType(work_line, dec_insn.vB_);
+ inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ CheckFinalFieldAccess(method, inst_field, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ field_class = inst_field->GetType();
+ if (field_class == NULL) {
+ LOG(ERROR) << "VFY: unable to recover field class from '"
+ << inst_field->GetName()->ToModifiedUtf8() << "'";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ value_type = GetRegisterType(work_line, dec_insn.vA_);
+ if (!RegTypeIsReference(value_type)) {
+ LOG(ERROR) << "VFY: storing non-ref v" << dec_insn.vA_
+ << " into ref field '"
+ << inst_field->GetName()->ToModifiedUtf8() << "' ("
+ << field_class->GetDescriptor()->ToModifiedUtf8() << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (value_type != kRegTypeZero) {
+ value_class = RegTypeInitializedReferenceToClass(value_type);
+ if (value_class == NULL) {
+ LOG(ERROR) << "VFY: storing uninit ref v" << dec_insn.vA_
+ << " into ref field";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ /* allow if field is any interface or field is base class */
+ if (!field_class->IsInterface() &&
+ !field_class->IsAssignableFrom(value_class)) {
+ Class* inst_field_class = inst_field->GetDeclaringClass();
+ LOG(ERROR) << "VFY: storing type '"
+ << value_class->GetDescriptor()->ToModifiedUtf8()
+ << "' into field type '"
+ << field_class->GetDescriptor()->ToModifiedUtf8()
+ << "' ("
+ << inst_field_class->GetDescriptor()->ToModifiedUtf8()
+ << "." << inst_field->GetName()->ToModifiedUtf8() << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ }
+ break;
+
+ case Instruction::SGET:
+ tmp_type = kRegTypeInteger;
+ goto sget_1nr_common;
+ case Instruction::SGET_BOOLEAN:
+ tmp_type = kRegTypeBoolean;
+ goto sget_1nr_common;
+ case Instruction::SGET_BYTE:
+ tmp_type = kRegTypeByte;
+ goto sget_1nr_common;
+ case Instruction::SGET_CHAR:
+ tmp_type = kRegTypeChar;
+ goto sget_1nr_common;
+ case Instruction::SGET_SHORT:
+ tmp_type = kRegTypeShort;
+ goto sget_1nr_common;
+sget_1nr_common:
+ {
+ Field* static_field;
+ RegType field_type;
+
+ static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /*
+ * Make sure the field's type is compatible with expectation.
+ * We can get ourselves into trouble if we mix & match loads
+ * and stores with different widths, so rather than just checking
+ * "CanConvertTo1nr" we require that the field types have equal
+ * widths.
+ */
+ field_type =
+ PrimitiveTypeToRegType(static_field->GetType()->GetPrimitiveType());
+
+ /* correct if float */
+ if (field_type == kRegTypeFloat)
+ tmp_type = kRegTypeFloat;
+
+ if (tmp_type != field_type) {
+ Class* static_field_class = static_field->GetDeclaringClass();
+ LOG(ERROR) << "VFY: invalid sget-1nr of "
+ << static_field_class->GetDescriptor()->ToModifiedUtf8()
+ << "." << static_field->GetName()->ToModifiedUtf8()
+ << " (inst=" << tmp_type << " actual=" << field_type
+ << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ SetRegisterType(work_line, dec_insn.vA_, tmp_type);
+ }
+ break;
+ case Instruction::SGET_WIDE:
+ {
+ Field* static_field;
+ RegType dst_type;
+
+ static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+ Class* static_field_class = static_field->GetDeclaringClass();
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ /* check the type, which should be prim */
+ switch (static_field->GetType()->GetPrimitiveType()) {
+ case Class::kPrimDouble:
+ dst_type = kRegTypeDoubleLo;
+ break;
+ case Class::kPrimLong:
+ dst_type = kRegTypeLongLo;
+ break;
+ default:
+ LOG(ERROR) << "VFY: invalid sget-wide of "
+ << static_field_class->GetDescriptor()->ToModifiedUtf8()
+ << "." << static_field->GetName()->ToModifiedUtf8();
+ dst_type = kRegTypeUnknown;
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (failure == VERIFY_ERROR_NONE) {
+ SetRegisterType(work_line, dec_insn.vA_, dst_type);
+ }
+ }
+ break;
+ case Instruction::SGET_OBJECT:
+ {
+ Field* static_field;
+ Class* field_class;
+
+ static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ field_class = static_field->GetType();
+ if (field_class == NULL) {
+ LOG(ERROR) << "VFY: unable to recover field class from '"
+ << static_field->GetName()->ToModifiedUtf8() << "'";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (field_class->IsPrimitive()) {
+ LOG(ERROR) << "VFY: attempt to get prim field with sget-object";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(field_class));
+ }
+ break;
+ case Instruction::SPUT:
+ tmp_type = kRegTypeInteger;
+ goto sput_1nr_common;
+ case Instruction::SPUT_BOOLEAN:
+ tmp_type = kRegTypeBoolean;
+ goto sput_1nr_common;
+ case Instruction::SPUT_BYTE:
+ tmp_type = kRegTypeByte;
+ goto sput_1nr_common;
+ case Instruction::SPUT_CHAR:
+ tmp_type = kRegTypeChar;
+ goto sput_1nr_common;
+ case Instruction::SPUT_SHORT:
+ tmp_type = kRegTypeShort;
+ goto sput_1nr_common;
+sput_1nr_common:
+ {
+ RegType src_type, field_type;
+ Field* static_field;
+
+ static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ CheckFinalFieldAccess(method, static_field, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /*
+ * Get type of field we're storing into. We know that the
+ * contents of the register match the instruction, but we also
+ * need to ensure that the instruction matches the field type.
+ * Using e.g. sput-short to write into a 32-bit integer field
+ * can lead to trouble if we do 16-bit writes.
+ */
+ field_type =
+ PrimitiveTypeToRegType(static_field->GetType()->GetPrimitiveType());
+ src_type = GetRegisterType(work_line, dec_insn.vA_);
+
+ /* correct if float */
+ if (field_type == kRegTypeFloat)
+ tmp_type = kRegTypeFloat;
+
+ /*
+ * compiler can generate synthetic functions that write byte values
+ * into boolean fields.
+ */
+ if (tmp_type == kRegTypeBoolean && src_type == kRegTypeByte)
+ tmp_type = kRegTypeByte;
+ if (field_type == kRegTypeBoolean && src_type == kRegTypeByte)
+ field_type = kRegTypeByte;
+
+ /* make sure the source register has the correct type */
+ if (!CanConvertTo1nr(src_type, tmp_type)) {
+ LOG(ERROR) << "VFY: invalid reg type " << src_type
+ << " on sput instr (need " << tmp_type << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ VerifyRegisterType(work_line, dec_insn.vA_, field_type, &failure);
+
+ if (failure != VERIFY_ERROR_NONE || field_type == kRegTypeUnknown ||
+ tmp_type != field_type) {
+ Class* static_field_class = static_field->GetDeclaringClass();
+ LOG(ERROR) << "VFY: invalid sput-1nr of "
+ << static_field_class->GetDescriptor()->ToModifiedUtf8()
+ << "." << static_field->GetName()->ToModifiedUtf8()
+ << " (inst=" << tmp_type << " actual=" << field_type
+ << ")";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ break;
+ case Instruction::SPUT_WIDE:
+ Field* static_field;
+
+ static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ CheckFinalFieldAccess(method, static_field, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /* check the type, which should be prim */
+ switch (static_field->GetType()->GetPrimitiveType()) {
+ case Class::kPrimDouble:
+ VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeDoubleLo,
+ &failure);
+ break;
+ case Class::kPrimLong:
+ VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeLongLo, &failure);
+ break;
+ default:
+ LOG(ERROR) << "VFY: invalid sput-wide of "
+ << static_field->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+ << "." << static_field->GetName()->ToModifiedUtf8();
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ break;
+ case Instruction::SPUT_OBJECT:
+ {
+ Class* field_class;
+ Class* value_class;
+ Field* static_field;
+ RegType value_type;
+
+ static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ CheckFinalFieldAccess(method, static_field, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ field_class = static_field->GetType();
+ if (field_class == NULL) {
+ LOG(ERROR) << "VFY: unable to recover field class from '"
+ << static_field->GetName()->ToModifiedUtf8() << "'";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ value_type = GetRegisterType(work_line, dec_insn.vA_);
+ if (!RegTypeIsReference(value_type)) {
+ LOG(ERROR) << "VFY: storing non-ref v" << dec_insn.vA_
+ << " into ref field '"
+ << static_field->GetName()->ToModifiedUtf8() << "' ("
+ << field_class->GetDescriptor()->ToModifiedUtf8() << ")",
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (value_type != kRegTypeZero) {
+ value_class = RegTypeInitializedReferenceToClass(value_type);
+ if (value_class == NULL) {
+ LOG(ERROR) << "VFY: storing uninit ref v" << dec_insn.vA_
+ << " into ref field";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ /* allow if field is any interface or field is base class */
+ if (!field_class->IsInterface() &&
+ !field_class->IsAssignableFrom(value_class)) {
+ Class* static_field_class = static_field->GetDeclaringClass();
+ LOG(ERROR) << "VFY: storing type '"
+ << value_class->GetDescriptor()->ToModifiedUtf8()
+ << "' into field type '"
+ << field_class->GetDescriptor()->ToModifiedUtf8()
+ << "' ("
+ << static_field_class->GetDescriptor()->ToModifiedUtf8()
+ << "." << static_field->GetName()->ToModifiedUtf8()
+ << ")",
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+ }
+ break;
+
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_SUPER_RANGE:
+ {
+ Method* called_method;
+ RegType return_type;
+ bool is_range;
+ bool is_super;
+
+ is_range = (dec_insn.opcode_ == Instruction::INVOKE_VIRTUAL_RANGE ||
+ dec_insn.opcode_ == Instruction::INVOKE_SUPER_RANGE);
+ is_super = (dec_insn.opcode_ == Instruction::INVOKE_SUPER ||
+ dec_insn.opcode_ == Instruction::INVOKE_SUPER_RANGE);
+
+ called_method = VerifyInvocationArgs(vdata, work_line, registers_size,
+ &dec_insn, METHOD_VIRTUAL, is_range, is_super, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ return_type = GetMethodReturnType(dex_file, called_method);
+ SetResultRegisterType(work_line, registers_size, return_type);
+ just_set_result = true;
+ }
+ break;
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ {
+ RegType return_type;
+ Method* called_method;
+ bool is_range;
+
+ is_range = (dec_insn.opcode_ == Instruction::INVOKE_DIRECT_RANGE);
+ called_method = VerifyInvocationArgs(vdata, work_line, registers_size,
+ &dec_insn, METHOD_DIRECT, is_range, false, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /*
+ * Some additional checks when calling <init>. We know from
+ * the invocation arg check that the "this" argument is an
+ * instance of called_method->klass. Now we further restrict
+ * that to require that called_method->klass is the same as
+ * this->klass or this->super, allowing the latter only if
+ * the "this" argument is the same as the "this" argument to
+ * this method (which implies that we're in <init> ourselves).
+ */
+ if (IsInitMethod(called_method)) {
+ RegType this_type;
+ this_type = GetInvocationThis(work_line, &dec_insn, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ /* no null refs allowed (?) */
+ if (this_type == kRegTypeZero) {
+ LOG(ERROR) << "VFY: unable to initialize null ref";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ Class* this_class;
+
+ this_class = RegTypeReferenceToClass(this_type, uninit_map);
+ assert(this_class != NULL);
+
+ /* must be in same class or in superclass */
+ if (called_method->GetDeclaringClass() == this_class->GetSuperClass())
+ {
+ if (this_class != method->GetDeclaringClass()) {
+ LOG(ERROR) << "VFY: invoke-direct <init> on super only "
+ << "allowed for 'this' in <init>";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ } else if (called_method->GetDeclaringClass() != this_class) {
+ LOG(ERROR) << "VFY: invoke-direct <init> must be on current "
+ << "class or super";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /* arg must be an uninitialized reference */
+ if (!RegTypeIsUninitReference(this_type)) {
+ LOG(ERROR) << "VFY: can only initialize the uninitialized";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /*
+ * Replace the uninitialized reference with an initialized
+ * one, and clear the entry in the uninit map. We need to
+ * do this for all registers that have the same object
+ * instance in them, not just the "this" register.
+ */
+ MarkRefsAsInitialized(work_line, registers_size, uninit_map,
+ this_type, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+ }
+ return_type = GetMethodReturnType(dex_file, called_method);
+ SetResultRegisterType(work_line, registers_size, return_type);
+ just_set_result = true;
+ }
+ break;
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE:
+ {
+ RegType return_type;
+ Method* called_method;
+ bool is_range;
+
+ is_range = (dec_insn.opcode_ == Instruction::INVOKE_STATIC_RANGE);
+ called_method = VerifyInvocationArgs(vdata, work_line, registers_size,
+ &dec_insn, METHOD_STATIC, is_range, false, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ return_type = GetMethodReturnType(dex_file, called_method);
+ SetResultRegisterType(work_line, registers_size, return_type);
+ just_set_result = true;
+ }
+ break;
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ {
+ RegType /*this_type,*/ return_type;
+ Method* abs_method;
+ bool is_range;
+
+ is_range = (dec_insn.opcode_ == Instruction::INVOKE_INTERFACE_RANGE);
+ abs_method = VerifyInvocationArgs(vdata, work_line, registers_size,
+ &dec_insn, METHOD_INTERFACE, is_range, false, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+#if 0 /* can't do this here, fails on dalvik test 052-verifier-fun */
+ /*
+ * Get the type of the "this" arg, which should always be an
+ * interface class. Because we don't do a full merge on
+ * interface classes, this might have reduced to Object.
+ */
+ this_type = GetInvocationThis(work_line, &dec_insn, &failure);
+ if (failure != VERIFY_ERROR_NONE)
+ break;
+
+ if (this_type == kRegTypeZero) {
+ /* null pointer always passes (and always fails at runtime) */
+ } else {
+ Class* this_class;
+
+ this_class = RegTypeInitializedReferenceToClass(this_type);
+ if (this_class == NULL) {
+ LOG(ERROR) << "VFY: interface call on uninitialized";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+
+ /*
+ * Either "this_class" needs to be the interface class that
+ * defined abs_method, or abs_method's class needs to be one
+ * of the interfaces implemented by "this_class". (Or, if
+ * we couldn't complete the merge, this will be Object.)
+ */
+ if (this_class != abs_method->GetDeclaringClass() &&
+ this_class != class_linker->FindSystemClass("Ljava/lang/Object;") &&
+ !this_class->Implements(abs_method->GetDeclaringClass())) {
+ LOG(ERROR) << "VFY: unable to match abs_method '"
+ << abs_method->GetName()->ToModifiedUtf8() << "' with "
+ << this_class->GetDescriptor()->ToModifiedUtf8()
+ << " interfaces";
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ }
+#endif
+
+ /*
+ * We don't have an object instance, so we can't find the
+ * concrete method. However, all of the type information is
+ * in the abstract method, so we're good.
+ */
+ return_type = GetMethodReturnType(dex_file, abs_method);
+ SetResultRegisterType(work_line, registers_size, return_type);
+ just_set_result = true;
+ }
+ break;
+
+ case Instruction::NEG_INT:
+ case Instruction::NOT_INT:
+ CheckUnop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+ &failure);
+ break;
+ case Instruction::NEG_LONG:
+ case Instruction::NOT_LONG:
+ CheckUnop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeLongLo, &failure);
+ break;
+ case Instruction::NEG_FLOAT:
+ CheckUnop(work_line, &dec_insn, kRegTypeFloat, kRegTypeFloat, &failure);
+ break;
+ case Instruction::NEG_DOUBLE:
+ CheckUnop(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeDoubleLo,
+ &failure);
+ break;
+ case Instruction::INT_TO_LONG:
+ CheckUnop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeInteger,
+ &failure);
+ break;
+ case Instruction::INT_TO_FLOAT:
+ CheckUnop(work_line, &dec_insn, kRegTypeFloat, kRegTypeInteger, &failure);
+ break;
+ case Instruction::INT_TO_DOUBLE:
+ CheckUnop(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeInteger,
+ &failure);
+ break;
+ case Instruction::LONG_TO_INT:
+ CheckUnop(work_line, &dec_insn, kRegTypeInteger, kRegTypeLongLo,
+ &failure);
+ break;
+ case Instruction::LONG_TO_FLOAT:
+ CheckUnop(work_line, &dec_insn, kRegTypeFloat, kRegTypeLongLo, &failure);
+ break;
+ case Instruction::LONG_TO_DOUBLE:
+ CheckUnop(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeLongLo,
+ &failure);
+ break;
+ case Instruction::FLOAT_TO_INT:
+ CheckUnop(work_line, &dec_insn, kRegTypeInteger, kRegTypeFloat, &failure);
+ break;
+ case Instruction::FLOAT_TO_LONG:
+ CheckUnop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeFloat, &failure);
+ break;
+ case Instruction::FLOAT_TO_DOUBLE:
+ CheckUnop(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeFloat,
+ &failure);
+ break;
+ case Instruction::DOUBLE_TO_INT:
+ CheckUnop(work_line, &dec_insn, kRegTypeInteger, kRegTypeDoubleLo,
+ &failure);
+ break;
+ case Instruction::DOUBLE_TO_LONG:
+ CheckUnop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeDoubleLo,
+ &failure);
+ break;
+ case Instruction::DOUBLE_TO_FLOAT:
+ CheckUnop(work_line, &dec_insn, kRegTypeFloat, kRegTypeDoubleLo,
+ &failure);
+ break;
+ case Instruction::INT_TO_BYTE:
+ CheckUnop(work_line, &dec_insn, kRegTypeByte, kRegTypeInteger, &failure);
+ break;
+ case Instruction::INT_TO_CHAR:
+ CheckUnop(work_line, &dec_insn, kRegTypeChar, kRegTypeInteger, &failure);
+ break;
+ case Instruction::INT_TO_SHORT:
+ CheckUnop(work_line, &dec_insn, kRegTypeShort, kRegTypeInteger, &failure);
+ break;
+
+ case Instruction::ADD_INT:
+ case Instruction::SUB_INT:
+ case Instruction::MUL_INT:
+ case Instruction::REM_INT:
+ case Instruction::DIV_INT:
+ case Instruction::SHL_INT:
+ case Instruction::SHR_INT:
+ case Instruction::USHR_INT:
+ CheckBinop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+ kRegTypeInteger, false, &failure);
+ break;
+ case Instruction::AND_INT:
+ case Instruction::OR_INT:
+ case Instruction::XOR_INT:
+ CheckBinop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+ kRegTypeInteger, true, &failure);
+ break;
+ case Instruction::ADD_LONG:
+ case Instruction::SUB_LONG:
+ case Instruction::MUL_LONG:
+ case Instruction::DIV_LONG:
+ case Instruction::REM_LONG:
+ case Instruction::AND_LONG:
+ case Instruction::OR_LONG:
+ case Instruction::XOR_LONG:
+ CheckBinop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeLongLo,
+ kRegTypeLongLo, false, &failure);
+ break;
+ case Instruction::SHL_LONG:
+ case Instruction::SHR_LONG:
+ case Instruction::USHR_LONG:
+ /* shift distance is Int, making these different from other binops */
+ CheckBinop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeLongLo,
+ kRegTypeInteger, false, &failure);
+ break;
+ case Instruction::ADD_FLOAT:
+ case Instruction::SUB_FLOAT:
+ case Instruction::MUL_FLOAT:
+ case Instruction::DIV_FLOAT:
+ case Instruction::REM_FLOAT:
+ CheckBinop(work_line, &dec_insn, kRegTypeFloat, kRegTypeFloat,
+ kRegTypeFloat, false, &failure);
+ break;
+ case Instruction::ADD_DOUBLE:
+ case Instruction::SUB_DOUBLE:
+ case Instruction::MUL_DOUBLE:
+ case Instruction::DIV_DOUBLE:
+ case Instruction::REM_DOUBLE:
+ CheckBinop(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeDoubleLo,
+ kRegTypeDoubleLo, false, &failure);
+ break;
+ case Instruction::ADD_INT_2ADDR:
+ case Instruction::SUB_INT_2ADDR:
+ case Instruction::MUL_INT_2ADDR:
+ case Instruction::REM_INT_2ADDR:
+ case Instruction::SHL_INT_2ADDR:
+ case Instruction::SHR_INT_2ADDR:
+ case Instruction::USHR_INT_2ADDR:
+ CheckBinop2addr(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+ kRegTypeInteger, false, &failure);
+ break;
+ case Instruction::AND_INT_2ADDR:
+ case Instruction::OR_INT_2ADDR:
+ case Instruction::XOR_INT_2ADDR:
+ CheckBinop2addr(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+ kRegTypeInteger, true, &failure);
+ break;
+ case Instruction::DIV_INT_2ADDR:
+ CheckBinop2addr(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+ kRegTypeInteger, false, &failure);
+ break;
+ case Instruction::ADD_LONG_2ADDR:
+ case Instruction::SUB_LONG_2ADDR:
+ case Instruction::MUL_LONG_2ADDR:
+ case Instruction::DIV_LONG_2ADDR:
+ case Instruction::REM_LONG_2ADDR:
+ case Instruction::AND_LONG_2ADDR:
+ case Instruction::OR_LONG_2ADDR:
+ case Instruction::XOR_LONG_2ADDR:
+ CheckBinop2addr(work_line, &dec_insn, kRegTypeLongLo, kRegTypeLongLo,
+ kRegTypeLongLo, false, &failure);
+ break;
+ case Instruction::SHL_LONG_2ADDR:
+ case Instruction::SHR_LONG_2ADDR:
+ case Instruction::USHR_LONG_2ADDR:
+ CheckBinop2addr(work_line, &dec_insn, kRegTypeLongLo, kRegTypeLongLo,
+ kRegTypeInteger, false, &failure);
+ break;
+ case Instruction::ADD_FLOAT_2ADDR:
+ case Instruction::SUB_FLOAT_2ADDR:
+ case Instruction::MUL_FLOAT_2ADDR:
+ case Instruction::DIV_FLOAT_2ADDR:
+ case Instruction::REM_FLOAT_2ADDR:
+ CheckBinop2addr(work_line, &dec_insn, kRegTypeFloat, kRegTypeFloat,
+ kRegTypeFloat, false, &failure);
+ break;
+ case Instruction::ADD_DOUBLE_2ADDR:
+ case Instruction::SUB_DOUBLE_2ADDR:
+ case Instruction::MUL_DOUBLE_2ADDR:
+ case Instruction::DIV_DOUBLE_2ADDR:
+ case Instruction::REM_DOUBLE_2ADDR:
+ CheckBinop2addr(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeDoubleLo,
+ kRegTypeDoubleLo, false, &failure);
+ break;
+ case Instruction::ADD_INT_LIT16:
+ case Instruction::RSUB_INT:
+ case Instruction::MUL_INT_LIT16:
+ case Instruction::DIV_INT_LIT16:
+ case Instruction::REM_INT_LIT16:
+ CheckLitop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger, false,
+ &failure);
+ break;
+ case Instruction::AND_INT_LIT16:
+ case Instruction::OR_INT_LIT16:
+ case Instruction::XOR_INT_LIT16:
+ CheckLitop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger, true,
+ &failure);
+ break;
+ case Instruction::ADD_INT_LIT8:
+ case Instruction::RSUB_INT_LIT8:
+ case Instruction::MUL_INT_LIT8:
+ case Instruction::DIV_INT_LIT8:
+ case Instruction::REM_INT_LIT8:
+ case Instruction::SHL_INT_LIT8:
+ CheckLitop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger, false,
+ &failure);
+ break;
+ case Instruction::SHR_INT_LIT8:
+ tmp_type = AdjustForRightShift(work_line, dec_insn.vB_, dec_insn.vC_,
+ false, &failure);
+ CheckLitop(work_line, &dec_insn, tmp_type, kRegTypeInteger, false,
+ &failure);
+ break;
+ case Instruction::USHR_INT_LIT8:
+ tmp_type = AdjustForRightShift(work_line, dec_insn.vB_, dec_insn.vC_,
+ true, &failure);
+ CheckLitop(work_line, &dec_insn, tmp_type, kRegTypeInteger, false,
+ &failure);
+ break;
+ case Instruction::AND_INT_LIT8:
+ case Instruction::OR_INT_LIT8:
+ case Instruction::XOR_INT_LIT8:
+ CheckLitop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger, true,
+ &failure);
+ break;
+
+ /*
+ * This falls into the general category of "optimized" instructions,
+ * which don't generally appear during verification. Because it's
+ * inserted in the course of verification, we can expect to see it here.
+ */
+ //case Instruction::THROW_VERIFICATION_ERROR:
+ case Instruction::UNUSED_ED:
+ break;
+
+ /*
+ * Verifying "quickened" instructions is tricky, because we have
+ * discarded the original field/method information. The byte offsets
+ * and vtable indices only have meaning in the context of an object
+ * instance.
+ *
+ * If a piece of code declares a local reference variable, assigns
+ * null to it, and then issues a virtual method call on it, we
+ * cannot evaluate the method call during verification. This situation
+ * isn't hard to handle, since we know the call will always result in an
+ * NPE, and the arguments and return value don't matter. Any code that
+ * depends on the result of the method call is inaccessible, so the
+ * fact that we can't fully verify anything that comes after the bad
+ * call is not a problem.
+ *
+ * We must also consider the case of multiple code paths, only some of
+ * which involve a null reference. We can completely verify the method
+ * if we sidestep the results of executing with a null reference.
+ * For example, if on the first pass through the code we try to do a
+ * virtual method invocation through a null ref, we have to skip the
+ * method checks and have the method return a "wildcard" type (which
+ * merges with anything to become that other thing). The move-result
+ * will tell us if it's a reference, single-word numeric, or double-word
+ * value. We continue to perform the verification, and at the end of
+ * the function any invocations that were never fully exercised are
+ * marked as null-only.
+ *
+ * We would do something similar for the field accesses. The field's
+ * type, once known, can be used to recover the width of short integers.
+ * If the object reference was null, the field-get returns the "wildcard"
+ * type, which is acceptable for any operation.
+ */
+ case Instruction::UNUSED_EE:
+ case Instruction::UNUSED_EF:
+ case Instruction::UNUSED_F2:
+ case Instruction::UNUSED_F3:
+ case Instruction::UNUSED_F4:
+ case Instruction::UNUSED_F5:
+ case Instruction::UNUSED_F6:
+ case Instruction::UNUSED_F7:
+ case Instruction::UNUSED_F8:
+ case Instruction::UNUSED_F9:
+ case Instruction::UNUSED_FA:
+ case Instruction::UNUSED_FB:
+ //case Instruction::EXECUTE_INLINE:
+ //case Instruction::EXECUTE_INLINE_RANGE:
+ //case Instruction::IGET_QUICK:
+ //case Instruction::IGET_WIDE_QUICK:
+ //case Instruction::IGET_OBJECT_QUICK:
+ //case Instruction::IPUT_QUICK:
+ //case Instruction::IPUT_WIDE_QUICK:
+ //case Instruction::IPUT_OBJECT_QUICK:
+ //case Instruction::INVOKE_VIRTUAL_QUICK:
+ //case Instruction::INVOKE_VIRTUAL_QUICK_RANGE:
+ //case Instruction::INVOKE_SUPER_QUICK:
+ //case Instruction::INVOKE_SUPER_QUICK_RANGE:
+ /* fall through to failure */
+
+ /*
+ * These instructions are equivalent (from the verifier's point of view)
+ * to the original form. The change was made for correctness rather
+ * than improved performance (except for invoke-object-init, which
+ * provides both). The substitution takes place after verification
+ * completes, though, so we don't expect to see them here.
+ */
+ case Instruction::UNUSED_F0:
+ case Instruction::UNUSED_F1:
+ case Instruction::UNUSED_E3:
+ case Instruction::UNUSED_E8:
+ case Instruction::UNUSED_E7:
+ case Instruction::UNUSED_E4:
+ case Instruction::UNUSED_E9:
+ case Instruction::UNUSED_FC:
+ case Instruction::UNUSED_E5:
+ case Instruction::UNUSED_EA:
+ case Instruction::UNUSED_FD:
+ case Instruction::UNUSED_E6:
+ case Instruction::UNUSED_EB:
+ case Instruction::UNUSED_FE:
+ //case Instruction::INVOKE_OBJECT_INIT_RANGE:
+ //case Instruction::RETURN_VOID_BARRIER:
+ //case Instruction::IGET_VOLATILE:
+ //case Instruction::IGET_WIDE_VOLATILE:
+ //case Instruction::IGET_OBJECT_VOLATILE:
+ //case Instruction::IPUT_VOLATILE:
+ //case Instruction::IPUT_WIDE_VOLATILE:
+ //case Instruction::IPUT_OBJECT_VOLATILE:
+ //case Instruction::SGET_VOLATILE:
+ //case Instruction::SGET_WIDE_VOLATILE:
+ //case Instruction::SGET_OBJECT_VOLATILE:
+ //case Instruction::SPUT_VOLATILE:
+ //case Instruction::SPUT_WIDE_VOLATILE:
+ //case Instruction::SPUT_OBJECT_VOLATILE:
+ /* fall through to failure */
+
+ /* These should never appear during verification. */
+ case Instruction::UNUSED_3E:
+ case Instruction::UNUSED_3F:
+ case Instruction::UNUSED_40:
+ case Instruction::UNUSED_41:
+ case Instruction::UNUSED_42:
+ case Instruction::UNUSED_43:
+ case Instruction::UNUSED_73:
+ case Instruction::UNUSED_79:
+ case Instruction::UNUSED_7A:
+ case Instruction::UNUSED_EC:
+ case Instruction::UNUSED_FF:
+ //case Instruction::BREAKPOINT:
+ //case Instruction::DISPATCH_FF:
+ failure = VERIFY_ERROR_GENERIC;
+ break;
+
+ /*
+ * DO NOT add a "default" clause here. Without it the compiler will
+ * complain if an instruction is missing (which is desirable).
+ */
+ }
+
+ if (failure != VERIFY_ERROR_NONE) {
+ //if (failure == VERIFY_ERROR_GENERIC || gDvm.optimizing)
+ if (failure == VERIFY_ERROR_GENERIC) {
+ /* immediate failure, reject class */
+ LOG(ERROR) << "VFY: rejecting opcode 0x" << std::hex
+ << (int) dec_insn.opcode_ << " at 0x" << insn_idx << std::dec;
+ return false;
+ } else {
+ /* replace opcode and continue on */
+ LOG(ERROR) << "VFY: replacing opcode 0x" << std::hex
+ << (int) dec_insn.opcode_ << " at 0x" << insn_idx << std::dec;
+ if (!ReplaceFailingInstruction(code_item, insn_flags, insn_idx, failure))
+ {
+ LOG(ERROR) << "VFY: rejecting opcode 0x" << std::hex
+ << (int) dec_insn.opcode_ << " at 0x" << insn_idx
+ << std::dec;
+ return false;
+ }
+ /* IMPORTANT: method->insns may have been changed */
+ insns = code_item->insns_ + insn_idx;
+
+ /* continue on as if we just handled a throw-verification-error */
+ failure = VERIFY_ERROR_NONE;
+ opcode_flag = Instruction::kThrow;
+ }
+ }
+
+ /*
+ * If we didn't just set the result register, clear it out. This
+ * ensures that you can only use "move-result" immediately after the
+ * result is set. (We could check this statically, but it's not
+ * expensive and it makes our debugging output cleaner.)
+ */
+ if (!just_set_result) {
+ int reg = RESULT_REGISTER(registers_size);
+ SetRegisterType(work_line, reg, kRegTypeUnknown);
+ SetRegisterType(work_line, reg + 1, kRegTypeUnknown);
+ }
+
+ /*
+ * Handle "continue". Tag the next consecutive instruction.
+ */
+ if ((opcode_flag & Instruction::kContinue) != 0) {
+ size_t insn_width = InsnGetWidth(insn_flags, insn_idx);
+ if (insn_idx + insn_width >= insns_size) {
+ LOG(ERROR) << "VFY: execution can walk off end of code area (from 0x"
+ << std::hex << insn_idx << std::dec << ")";
+ return false;
+ }
+
+ /*
+ * The only way to get to a move-exception instruction is to get
+ * thrown there. Make sure the next instruction isn't one.
+ */
+ if (!CheckMoveException(code_item->insns_, insn_idx + insn_width))
+ return false;
+
+ if (GetRegisterLine(reg_table, insn_idx + insn_width)->reg_types_ != NULL) {
+ /*
+ * Merge registers into what we have for the next instruction,
+ * and set the "changed" flag if needed.
+ */
+ if (!UpdateRegisters(insn_flags, reg_table, insn_idx + insn_width,
+ work_line))
+ return false;
+ } else {
+ /*
+ * We're not recording register data for the next instruction,
+ * so we don't know what the prior state was. We have to
+ * assume that something has changed and re-evaluate it.
+ */
+ InsnSetChanged(insn_flags, insn_idx + insn_width, true);
+ }
+ }
+
+ /*
+ * Handle "branch". Tag the branch target.
+ *
+ * NOTE: instructions like Instruction::EQZ provide information about the
+ * state of the register when the branch is taken or not taken. For example,
+ * somebody could get a reference field, check it for zero, and if the
+ * branch is taken immediately store that register in a boolean field
+ * since the value is known to be zero. We do not currently account for
+ * that, and will reject the code.
+ *
+ * TODO: avoid re-fetching the branch target
+ */
+ if ((opcode_flag & Instruction::kBranch) != 0) {
+ bool isConditional, selfOkay;
+
+ if (!GetBranchOffset(code_item, insn_flags, insn_idx, &branch_target,
+ &isConditional, &selfOkay)) {
+ /* should never happen after static verification */
+ LOG(ERROR) << "VFY: bad branch at 0x" << std::hex << insn_idx << std::dec;
+ return false;
+ }
+ assert(isConditional || (opcode_flag & Instruction::kContinue) == 0);
+ assert(!isConditional || (opcode_flag & Instruction::kContinue) != 0);
+
+ if (!CheckMoveException(code_item->insns_, insn_idx + branch_target))
+ return false;
+
+ /* update branch target, set "changed" if appropriate */
+ if (!UpdateRegisters(insn_flags, reg_table, insn_idx + branch_target,
+ work_line))
+ return false;
+ }
+
+ /*
+ * Handle "switch". Tag all possible branch targets.
+ *
+ * We've already verified that the table is structurally sound, so we
+ * just need to walk through and tag the targets.
+ */
+ if ((opcode_flag & Instruction::kSwitch) != 0) {
+ int offset_to_switch = insns[1] | (((int32_t) insns[2]) << 16);
+ const uint16_t* switch_insns = insns + offset_to_switch;
+ int switch_count = switch_insns[1];
+ int offset_to_targets, targ;
+
+ if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
+ /* 0 = sig, 1 = count, 2/3 = first key */
+ offset_to_targets = 4;
+ } else {
+ /* 0 = sig, 1 = count, 2..count * 2 = keys */
+ assert((*insns & 0xff) == Instruction::SPARSE_SWITCH);
+ offset_to_targets = 2 + 2 * switch_count;
+ }
+
+ /* verify each switch target */
+ for (targ = 0; targ < switch_count; targ++) {
+ int offset;
+ uint32_t abs_offset;
+
+ /* offsets are 32-bit, and only partly endian-swapped */
+ offset = switch_insns[offset_to_targets + targ * 2] |
+ (((int32_t) switch_insns[offset_to_targets + targ * 2 + 1]) << 16);
+ abs_offset = insn_idx + offset;
+
+ assert(abs_offset < insns_size);
+
+ if (!CheckMoveException(code_item->insns_, abs_offset))
+ return false;
+
+ if (!UpdateRegisters(insn_flags, reg_table, abs_offset, work_line))
+ return false;
+ }
+ }
+
+ /*
+ * Handle instructions that can throw and that are sitting in a
+ * "try" block. (If they're not in a "try" block when they throw,
+ * control transfers out of the method.)
+ */
+ if ((opcode_flag & Instruction::kThrow) != 0 &&
+ InsnIsInTry(insn_flags, insn_idx)) {
+ bool has_catch_all = false;
+ DexFile::CatchHandlerIterator iterator = DexFile::dexFindCatchHandler(
+ *code_item, insn_idx);
+
+ for (; !iterator.HasNext(); iterator.Next()) {
+ if (iterator.Get().type_idx_ == DexFile::kDexNoIndex)
+ has_catch_all = true;
+
+ /*
+ * Merge registers into the "catch" block. We want to
+ * use the "savedRegs" rather than "work_regs", because
+ * at runtime the exception will be thrown before the
+ * instruction modifies any registers.
+ */
+ if (!UpdateRegisters(insn_flags, reg_table, iterator.Get().address_,
+ ®_table->saved_line_))
+ return false;
+ }
+
+ /*
+ * If the monitor stack depth is nonzero, there must be a "catch all"
+ * handler for this instruction. This does apply to monitor-exit
+ * because of async exception handling.
+ */
+ if (work_line->monitor_stack_top_ != 0 && !has_catch_all) {
+ /*
+ * The state in work_line reflects the post-execution state.
+ * If the current instruction is a monitor-enter and the monitor
+ * stack was empty, we don't need a catch-all (if it throws,
+ * it will do so before grabbing the lock).
+ */
+ if (!(dec_insn.opcode_ == Instruction::MONITOR_ENTER &&
+ work_line->monitor_stack_top_ == 1))
+ {
+ LOG(ERROR) << "VFY: no catch-all for instruction at 0x" << std::hex
+ << insn_idx << std::dec;
+ return false;
+ }
+ }
+ }
+
+ /*
+ * If we're returning from the method, make sure our monitor stack is empty.
+ */
+ if ((opcode_flag & Instruction::kReturn) != 0 &&
+ work_line->monitor_stack_top_ != 0) {
+ LOG(ERROR) << "VFY: return with stack depth="
+ << work_line->monitor_stack_top_ << " at 0x" << std::hex
+ << insn_idx << std::dec;
+ return false;
+ }
+
+ /*
+ * Update start_guess. Advance to the next instruction of that's
+ * possible, otherwise use the branch target if one was found. If
+ * neither of those exists we're in a return or throw; leave start_guess
+ * alone and let the caller sort it out.
+ */
+ if ((opcode_flag & Instruction::kContinue) != 0) {
+ *start_guess = insn_idx + InsnGetWidth(insn_flags, insn_idx);
+ } else if ((opcode_flag & Instruction::kBranch) != 0) {
+ /* we're still okay if branch_target is zero */
+ *start_guess = insn_idx + branch_target;
+ }
+
+ assert(*start_guess < insns_size &&
+ InsnGetWidth(insn_flags, *start_guess) != 0);
+
+ return true;
+}
+
+bool DexVerifier::ReplaceFailingInstruction(const DexFile::CodeItem* code_item,
+ InsnFlags* insn_flags, int insn_idx, VerifyError failure) {
+ const uint16_t* insns = code_item->insns_ + insn_idx;
+ const byte* ptr = reinterpret_cast<const byte*>(insns);
+ const Instruction* inst = Instruction::At(ptr);
+ Instruction::Code opcode = inst->Opcode();
+ VerifyErrorRefType ref_type;
+
+ /*
+ * Generate the new instruction out of the old.
+ *
+ * First, make sure this is an instruction we're expecting to stomp on.
+ */
+ switch (opcode) {
+ case Instruction::CONST_CLASS: // insn[1] == class ref, 2 bytes
+ case Instruction::CHECK_CAST:
+ case Instruction::INSTANCE_OF:
+ case Instruction::NEW_INSTANCE:
+ case Instruction::NEW_ARRAY:
+ case Instruction::FILLED_NEW_ARRAY: // insn[1] == class ref, 3 bytes
+ case Instruction::FILLED_NEW_ARRAY_RANGE:
+ ref_type = VERIFY_ERROR_REF_CLASS;
+ break;
+
+ case Instruction::IGET: // insn[1] == field ref, 2 bytes
+ case Instruction::IGET_BOOLEAN:
+ case Instruction::IGET_BYTE:
+ case Instruction::IGET_CHAR:
+ case Instruction::IGET_SHORT:
+ case Instruction::IGET_WIDE:
+ case Instruction::IGET_OBJECT:
+ case Instruction::IPUT:
+ case Instruction::IPUT_BOOLEAN:
+ case Instruction::IPUT_BYTE:
+ case Instruction::IPUT_CHAR:
+ case Instruction::IPUT_SHORT:
+ case Instruction::IPUT_WIDE:
+ case Instruction::IPUT_OBJECT:
+ case Instruction::SGET:
+ case Instruction::SGET_BOOLEAN:
+ case Instruction::SGET_BYTE:
+ case Instruction::SGET_CHAR:
+ case Instruction::SGET_SHORT:
+ case Instruction::SGET_WIDE:
+ case Instruction::SGET_OBJECT:
+ case Instruction::SPUT:
+ case Instruction::SPUT_BOOLEAN:
+ case Instruction::SPUT_BYTE:
+ case Instruction::SPUT_CHAR:
+ case Instruction::SPUT_SHORT:
+ case Instruction::SPUT_WIDE:
+ case Instruction::SPUT_OBJECT:
+ ref_type = VERIFY_ERROR_REF_FIELD;
+ break;
+
+ case Instruction::INVOKE_VIRTUAL: // insn[1] == method ref, 3 bytes
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_SUPER_RANGE:
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE:
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ ref_type = VERIFY_ERROR_REF_METHOD;
+ break;
+
+ default:
+ /* could handle this in a generic way, but this is probably safer */
+ LOG(ERROR) << "GLITCH: verifier asked to replace opcode 0x" << std::hex
+ << (int) opcode << std::dec;
+ return false;
+ }
+
+ assert(inst->IsThrow());
+
+ /* write a NOP over the third code unit, if necessary */
+ int width = InsnGetWidth(insn_flags, insn_idx);
+ switch (width) {
+ case 2:
+ /* nothing to do */
+ break;
+ case 3:
+ // TODO: Add this functionality
+ //UpdateCodeUnit(method, insns + 2, Instruction::NOP);
+ break;
+ default:
+ /* whoops */
+ LOG(FATAL) << "ERROR: stomped a " << width
+ << "-unit instruction with a verifier error";
+ }
+
+ /* encode the opcode, with the failure code in the high byte */
+ // TODO: REPLACE FAILING OPCODES
+ //assert(width == 2 || width == 3);
+ //uint16_t new_val = Instruction::THROW_VERIFICATION_ERROR |
+ //(failure << 8) | (ref_type << (8 + kVerifyErrorRefTypeShift));
+ //UpdateCodeUnit(method, insns, new_val);
+
+ return true;
+}
+
+/* Handle a monitor-enter instruction. */
+void DexVerifier::HandleMonitorEnter(RegisterLine* work_line, uint32_t reg_idx,
+ uint32_t insn_idx, VerifyError* failure) {
+ if (!RegTypeIsReference(GetRegisterType(work_line, reg_idx))) {
+ LOG(ERROR) << "VFY: monitor-enter on non-object";
+ *failure = VERIFY_ERROR_GENERIC;
+ return;
+ }
+
+ if (work_line->monitor_entries_ == NULL) {
+ return;
+ }
+
+ if (work_line->monitor_stack_top_ == kMaxMonitorStackDepth) {
+ LOG(ERROR) << "VFY: monitor-enter stack overflow (" << kMaxMonitorStackDepth
+ << ")";
+ *failure = VERIFY_ERROR_GENERIC;
+ return;
+ }
+
+ /*
+ * Push an entry on the stack, and set a bit in the register flags to
+ * indicate that it's associated with this register.
+ */
+ work_line->monitor_entries_[reg_idx] |= 1 << work_line->monitor_stack_top_;
+ work_line->monitor_stack_[work_line->monitor_stack_top_++] = insn_idx;
+}
+
+/* Handle a monitor-exit instruction. */
+void DexVerifier::HandleMonitorExit(RegisterLine* work_line, uint32_t reg_idx,
+ uint32_t insn_idx, VerifyError* failure) {
+ if (!RegTypeIsReference(GetRegisterType(work_line, reg_idx))) {
+ LOG(ERROR) << "VFY: monitor-exit on non-object";
+ *failure = VERIFY_ERROR_GENERIC;
+ return;
+ }
+
+ if (work_line->monitor_entries_ == NULL) {
+ return;
+ }
+
+ if (work_line->monitor_stack_top_ == 0) {
+ LOG(ERROR) << "VFY: monitor-exit stack underflow";
+ *failure = VERIFY_ERROR_GENERIC;
+ return;
+ }
+
+ /*
+ * Confirm that the entry at the top of the stack is associated with
+ * the register. Pop the top entry off.
+ */
+ work_line->monitor_stack_top_--;
+#ifdef BUG_3215458_FIXED
+ /*
+ * TODO: This code can safely be enabled if know we are working on
+ * a dex file of format version 036 or later. (That is, we'll need to
+ * add a check for the version number.)
+ */
+ if ((work_line->monitor_entries_[reg_idx] &
+ (1 << work_line->monitor_stack_top_)) == 0) {
+ LOG(ERROR) << "VFY: monitor-exit bit " << work_line->monitor_stack_top_
+ << " not set: addr=0x" << std::hex << insn_idx << std::dec
+ << " (bits[" << reg_idx << "]=" << std::hex
+ << work_line->monitor_entries_[reg_idx] << std::dec << ")";
+ *failure = VERIFY_ERROR_GENERIC;
+ return;
+ }
+#endif
+ work_line->monitor_stack_[work_line->monitor_stack_top_] = 0;
+
+ /* Clear the bit from the register flags. */
+ work_line->monitor_entries_[reg_idx] &= ~(1 << work_line->monitor_stack_top_);
+}
+
+Field* DexVerifier::GetInstField(VerifierData* vdata, RegType obj_type,
+ int field_idx, VerifyError* failure) {
+ Method* method = vdata->method_;
+ const DexFile* dex_file = vdata->dex_file_;
+ UninitInstanceMap* uninit_map = vdata->uninit_map_;
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
+ const ClassLoader* class_loader =
+ method->GetDeclaringClass()->GetClassLoader();
+ Field* field = NULL;
+ Class* obj_class;
+ bool must_be_local = false;
+
+ if (!RegTypeIsReference(obj_type)) {
+ LOG(ERROR) << "VFY: attempt to access field in non-reference type "
+ << obj_type;
+ *failure = VERIFY_ERROR_GENERIC;
+ return field;
+ }
+
+ field = class_linker->ResolveField(*dex_file, field_idx, dex_cache,
+ class_loader, false);
+ if (field == NULL) {
+ LOG(ERROR) << "VFY: unable to resolve instance field " << field_idx;
+ return field;
+ }
+
+ if (obj_type == kRegTypeZero)
+ return field;
+
+ /*
+ * Access to fields in uninitialized objects is allowed if this is
+ * the <init> method for the object and the field in question is
+ * declared by this class.
+ */
+ obj_class = RegTypeReferenceToClass(obj_type, uninit_map);
+ assert(obj_class != NULL);
+ if (RegTypeIsUninitReference(obj_type)) {
+ if (!IsInitMethod(method) || method->GetDeclaringClass() != obj_class) {
+ LOG(ERROR) << "VFY: attempt to access field via uninitialized ref";
+ *failure = VERIFY_ERROR_GENERIC;
+ return field;
+ }
+ must_be_local = true;
+ }
+
+ //if (!obj_class->InstanceOf(field->GetDeclaringClass())) {
+ if (!field->GetDeclaringClass()->IsAssignableFrom(obj_class)) {
+ LOG(ERROR) << "VFY: invalid field access (field "
+ << field->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+ << "." << field->GetName()->ToModifiedUtf8() << ", through "
+ << obj_class->GetDescriptor()->ToModifiedUtf8() << " ref)";
+ *failure = VERIFY_ERROR_NO_FIELD;
+ return field;
+ }
+
+ if (must_be_local) {
+ bool found = false;
+ /* for uninit ref, make sure it's defined by this class, not super */
+ for (uint32_t i = 0; i < obj_class->NumInstanceFields(); i++) {
+ found |= (field == obj_class->GetInstanceField(i));
+ }
+ if (!found) {
+ LOG(ERROR) << "VFY: invalid constructor field access (field "
+ << field->GetName()->ToModifiedUtf8() << " in "
+ << obj_class->GetDescriptor()->ToModifiedUtf8() << ")";
+ *failure = VERIFY_ERROR_GENERIC;
+ return field;
+ }
+ }
+
+ return field;
+}
+
+Field* DexVerifier::GetStaticField(VerifierData* vdata, int field_idx,
+ VerifyError* failure) {
+ Method* method = vdata->method_;
+ const DexFile* dex_file = vdata->dex_file_;
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
+ const ClassLoader* class_loader =
+ method->GetDeclaringClass()->GetClassLoader();
+ Field* field;
+
+ field = class_linker->ResolveField(*dex_file, field_idx, dex_cache,
+ class_loader, true);
+ if (field == NULL) {
+ //const DexFile::FieldId field_id = dex_file->GetFieldId(field_idx);
+
+ //LOG(ERROR) << "VFY: unable to resolve static field " << field_idx << " ("
+ //<< dex_file->GetFieldName(field_id) << ") in "
+ //<< dex_file->GetFieldClassDescriptor(field_id);
+ LOG(ERROR) << "VFY: unable to resolve static field";
+ }
+
+ return field;
+}
+
+Class* DexVerifier::GetCaughtExceptionType(VerifierData* vdata, int insn_idx,
+ VerifyError* failure) {
+ const DexFile* dex_file = vdata->dex_file_;
+ const DexFile::CodeItem* code_item = vdata->code_item_;
+ Method* method = vdata->method_;
+ Class* common_super = NULL;
+ uint32_t handlers_size;
const byte* handlers_ptr = DexFile::dexGetCatchHandlerData(*code_item, 0);
- uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
- for (uint32_t idx = 0; idx < handlers_size; idx++) {
+
+ if (code_item->tries_size_ != 0) {
+ handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+ } else {
+ handlers_size = 0;
+ }
+
+ for (uint32_t i = 0; i < handlers_size; i++) {
DexFile::CatchHandlerIterator iterator(handlers_ptr);
for (; !iterator.HasNext(); iterator.Next()) {
- uint32_t addr = iterator.Get().address_;
- if (InsnGetWidth(insn_flags, addr) == 0) {
- LOG(ERROR) << "VFY: exception handler starts at bad address ("
- << addr << ")";
- return false;
- }
+ DexFile::CatchHandlerItem handler = iterator.Get();
+ if (handler.address_ == (uint32_t) insn_idx) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Class* klass;
- InsnSetBranchTarget(insn_flags, addr, true);
+ if (handler.type_idx_ == DexFile::kDexNoIndex) {
+ klass = class_linker->FindSystemClass("Ljava/lang/Throwable;");
+ } else {
+ klass = class_linker->ResolveType(*dex_file, handler.type_idx_,
+ method->GetDeclaringClass());
+ }
+
+ if (klass == NULL) {
+ LOG(ERROR) << "VFY: unable to resolve exception class "
+ << handler.type_idx_ << " ("
+ << dex_file->dexStringByTypeIdx(handler.type_idx_) << ")";
+ /* TODO: do we want to keep going? If we don't fail
+ * this we run the risk of having a non-Throwable
+ * introduced at runtime. However, that won't pass
+ * an instanceof test, so is essentially harmless.
+ */
+ } else {
+ if (common_super == NULL)
+ common_super = klass;
+ else
+ common_super = FindCommonSuperclass(klass, common_super);
+ }
+ }
}
handlers_ptr = iterator.GetData();
}
- return true;
+ if (common_super == NULL) {
+ /* no catch blocks, or no catches with classes we can find */
+ LOG(ERROR) << "VFY: unable to find exception handler at addr 0x" << std::hex
+ << insn_idx << std::dec;
+ *failure = VERIFY_ERROR_GENERIC;
+ }
+
+ return common_super;
}
-
-
-bool DexVerify::VerifyClass(Class* klass) {
- if (klass->IsVerified()) {
- return true;
- }
- for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
- Method* method = klass->GetDirectMethod(i);
- if (!VerifyMethod(method)) {
- LOG(ERROR) << "Verifier rejected class "
- << klass->GetDescriptor()->ToModifiedUtf8();
- return false;
- }
- }
- for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
- Method* method = klass->GetVirtualMethod(i);
- if (!VerifyMethod(method)) {
- LOG(ERROR) << "Verifier rejected class "
- << klass->GetDescriptor()->ToModifiedUtf8();
- return false;
- }
- }
- return true;
+DexVerifier::RegType DexVerifier::GetMethodReturnType(const DexFile* dex_file,
+ const Method* method) {
+ Class* klass = method->GetReturnType();
+ if (klass->IsPrimitive())
+ return PrimitiveTypeToRegType(klass->GetPrimitiveType());
+ else
+ return RegTypeFromClass(klass);
}
-bool DexVerify::VerifyMethod(Method* method) {
- const DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
- const DexFile::CodeItem *code_item = dex_file.GetCodeItem(method->GetCodeItemOffset());
+Class* DexVerifier::GetClassFromRegister(const RegisterLine* register_line,
+ uint32_t vsrc, VerifyError* failure) {
+ /* get the element type of the array held in vsrc */
+ RegType type = GetRegisterType(register_line, vsrc);
- /*
- * If there aren't any instructions, make sure that's expected, then
- * exit successfully.
- */
- if (code_item == NULL) {
- if (!method->IsNative() && !method->IsAbstract()) {
- LOG(ERROR) << "VFY: zero-length code in concrete non-native method";
- return false;
- }
- return true;
+ /* if "always zero", we allow it to fail at runtime */
+ if (type == kRegTypeZero)
+ return NULL;
+
+ if (!RegTypeIsReference(type)) {
+ LOG(ERROR) << "VFY: tried to get class from non-ref register v" << vsrc
+ << " (type=" << type << ")",
+ *failure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+ if (RegTypeIsUninitReference(type)) {
+ LOG(ERROR) << "VFY: register " << vsrc << " holds uninitialized reference";
+ *failure = VERIFY_ERROR_GENERIC;
+ return NULL;
}
- /*
- * Sanity-check the register counts. ins + locals = registers, so make
- * sure that ins <= registers.
- */
- if (code_item->ins_size_ > code_item->registers_size_) {
- LOG(ERROR) << "VFY: bad register counts (ins=" << code_item->ins_size_
- << " regs=" << code_item->registers_size_;
- return false;
- }
-
- /*
- * Allocate and initialize an array to hold instruction data.
- */
- UniquePtr<uint32_t[]> insn_flags(new uint32_t[code_item->insns_size_]());
-
- /*
- * Run through the instructions and see if the width checks out.
- */
- if (!CheckInsnWidth(code_item->insns_, code_item->insns_size_, insn_flags.get())) {
- return false;
- }
-
- /*
- * Flag instructions guarded by a "try" block and check exception handlers.
- */
- if (!ScanTryCatchBlocks(code_item, insn_flags.get())) {
- return false;
- }
-
- /*
- * Perform static instruction verification.
- */
- if (!VerifyInstructions(&dex_file, code_item, insn_flags.get())) {
- return false;
- }
-
- /*
- * TODO: Code flow analysis
- */
-
- return true;
+ return RegTypeInitializedReferenceToClass(type);
}
-bool DexVerify::VerifyInstructions(const DexFile* dex_file,
- const DexFile::CodeItem* code_item, uint32_t insn_flags[]) {
- const byte* ptr = reinterpret_cast<const byte*>(code_item->insns_);
- const Instruction* inst = Instruction::At(ptr);
-
- /* Flag the start of the method as a branch target. */
- InsnSetBranchTarget(insn_flags, 0, true);
-
- uint32_t width = 0;
- uint32_t insns_size = code_item->insns_size_;
-
- while (width < insns_size) {
- if (!VerifyInstruction(dex_file, inst, width, code_item, insn_flags)) {
- LOG(ERROR) << "VFY: rejecting opcode 0x" << std::hex
- << (int) inst->Opcode() << " at 0x" << width << std::dec;
- return false;
- }
-
- /* Flag instructions that are garbage collection points */
- if (inst->IsBranch() || inst->IsSwitch() || inst->IsThrow() ||
- inst->IsReturn()) {
- InsnSetGcPoint(insn_flags, width, true);
- }
-
- width += inst->Size();
- inst = inst->Next();
+DexVerifier::RegType DexVerifier::GetInvocationThis(
+ const RegisterLine* register_line,
+ const Instruction::DecodedInstruction* dec_insn, VerifyError* failure) {
+ if (dec_insn->vA_ < 1) {
+ LOG(ERROR) << "VFY: invoke lacks 'this'";
+ *failure = VERIFY_ERROR_GENERIC;
+ return kRegTypeUnknown;
}
- return true;
+
+ /* get the element type of the array held in vsrc */
+ RegType this_type = GetRegisterType(register_line, dec_insn->vC_);
+ if (!RegTypeIsReference(this_type)) {
+ LOG(ERROR) << "VFY: tried to get class from non-ref register v"
+ << dec_insn->vC_ << " (type=" << this_type << ")";
+ *failure = VERIFY_ERROR_GENERIC;
+ return kRegTypeUnknown;
+ }
+
+ return this_type;
}
-bool DexVerify::VerifyInstruction(const DexFile* dex_file,
- const Instruction* inst, uint32_t code_offset,
- const DexFile::CodeItem* code_item, uint32_t insn_flags[]) {
- Instruction::Code opcode = inst->Opcode();
- bool result = true;
- uint32_t vA, vB, vC;
- uint64_t vB_wide;
- uint32_t arg[5];
+void DexVerifier::SetRegisterType(RegisterLine* register_line, uint32_t vdst,
+ RegType new_type) {
+ RegType* insn_regs = register_line->reg_types_;
- inst->Decode(vA, vB, vB_wide, vC, arg);
-
- int argumentA = inst->GetVerifyTypeArgumentA();
- int argumentB = inst->GetVerifyTypeArgumentB();
- int argumentC = inst->GetVerifyTypeArgumentC();
- int extra_flags = inst->GetVerifyExtraFlags();
-
- switch (argumentA) {
- case Instruction::kVerifyRegA:
- result &= CheckRegisterIndex(code_item, vA);
+ switch (new_type) {
+ case kRegTypeUnknown:
+ case kRegTypeBoolean:
+ case kRegTypeOne:
+ case kRegTypeConstByte:
+ case kRegTypeConstPosByte:
+ case kRegTypeConstShort:
+ case kRegTypeConstPosShort:
+ case kRegTypeConstChar:
+ case kRegTypeConstInteger:
+ case kRegTypeByte:
+ case kRegTypePosByte:
+ case kRegTypeShort:
+ case kRegTypePosShort:
+ case kRegTypeChar:
+ case kRegTypeInteger:
+ case kRegTypeFloat:
+ case kRegTypeZero:
+ case kRegTypeUninit:
+ insn_regs[vdst] = new_type;
break;
- case Instruction::kVerifyRegAWide:
- result &= CheckWideRegisterIndex(code_item, vA);
+ case kRegTypeConstLo:
+ case kRegTypeLongLo:
+ case kRegTypeDoubleLo:
+ insn_regs[vdst] = new_type;
+ insn_regs[vdst + 1] = new_type + 1;
+ break;
+ case kRegTypeConstHi:
+ case kRegTypeLongHi:
+ case kRegTypeDoubleHi:
+ /* should never set these explicitly */
+ LOG(FATAL) << "BUG: explicit set of high register type";
+ break;
+
+ default:
+ /* can't switch for ref types, so we check explicitly */
+ if (RegTypeIsReference(new_type)) {
+ insn_regs[vdst] = new_type;
+
+ /*
+ * In most circumstances we won't see a reference to a primitive
+ * class here (e.g. "D"), since that would mean the object in the
+ * register is actually a primitive type. It can happen as the
+ * result of an assumed-successful check-cast instruction in
+ * which the second argument refers to a primitive class. (In
+ * practice, such an instruction will always throw an exception.)
+ *
+ * This is not an issue for instructions like const-class, where
+ * the object in the register is a java.lang.Class instance.
+ */
+ break;
+ }
+ /* bad type - fall through */
+
+ case kRegTypeConflict: // should only be set during a merge
+ LOG(FATAL) << "BUG: set register to unknown type " << new_type;
break;
}
- switch (argumentB) {
- case Instruction::kVerifyRegB:
- result &= CheckRegisterIndex(code_item, vB);
+ /*
+ * Clear the monitor entry bits for this register.
+ */
+ if (register_line->monitor_entries_ != NULL)
+ register_line->monitor_entries_[vdst] = 0;
+}
+
+void DexVerifier::VerifyRegisterType(RegisterLine* register_line, uint32_t vsrc,
+ RegType check_type, VerifyError* failure) {
+ const RegType* insn_regs = register_line->reg_types_;
+ RegType src_type = insn_regs[vsrc];
+
+ switch (check_type) {
+ case kRegTypeFloat:
+ case kRegTypeBoolean:
+ case kRegTypePosByte:
+ case kRegTypeByte:
+ case kRegTypePosShort:
+ case kRegTypeShort:
+ case kRegTypeChar:
+ case kRegTypeInteger:
+ if (!CanConvertTo1nr(src_type, check_type)) {
+ LOG(ERROR) << "VFY: register1 v" << vsrc << " type " << src_type
+ << ", wanted " << check_type;
+ *failure = VERIFY_ERROR_GENERIC;
+ }
+ /* Update type if result is float */
+ if (check_type == kRegTypeFloat) {
+ SetRegisterType(register_line, vsrc, check_type);
+ } else {
+ /* Update const type to actual type after use */
+ SetRegisterType(register_line, vsrc, ConstTypeToRegType(src_type));
+ }
break;
- case Instruction::kVerifyRegBField:
- result &= CheckFieldIndex(dex_file, vB);
+ case kRegTypeLongLo:
+ case kRegTypeDoubleLo:
+ if (insn_regs[vsrc + 1] != src_type + 1) {
+ LOG(ERROR) << "VFY: register2 v" << vsrc << "-" << vsrc + 1
+ << " values " << insn_regs[vsrc] << ","
+ << insn_regs[vsrc + 1];
+ *failure = VERIFY_ERROR_GENERIC;
+ } else if (!CanConvertTo2(src_type, check_type)) {
+ LOG(ERROR) << "VFY: register2 v" << vsrc << " type " << src_type
+ << ", wanted " << check_type;
+ *failure = VERIFY_ERROR_GENERIC;
+ }
+ /* Update type if source is from const */
+ if (src_type == kRegTypeConstLo) {
+ SetRegisterType(register_line, vsrc, check_type);
+ }
break;
- case Instruction::kVerifyRegBMethod:
- result &= CheckMethodIndex(dex_file, vB);
- break;
- case Instruction::kVerifyRegBNewInstance:
- result &= CheckNewInstance(dex_file, vB);
- break;
- case Instruction::kVerifyRegBString:
- result &= CheckStringIndex(dex_file, vB);
- break;
- case Instruction::kVerifyRegBType:
- result &= CheckTypeIndex(dex_file, vB);
- break;
- case Instruction::kVerifyRegBWide:
- result &= CheckWideRegisterIndex(code_item, vB);
+ case kRegTypeConstLo:
+ case kRegTypeConstHi:
+ case kRegTypeLongHi:
+ case kRegTypeDoubleHi:
+ case kRegTypeZero:
+ case kRegTypeOne:
+ case kRegTypeUnknown:
+ case kRegTypeConflict:
+ /* should never be checking for these explicitly */
+ assert(false);
+ *failure = VERIFY_ERROR_GENERIC;
+ return;
+ case kRegTypeUninit:
+ default:
+ /* make sure check_type is initialized reference */
+ if (!RegTypeIsReference(check_type)) {
+ LOG(ERROR) << "VFY: unexpected check type " << check_type;
+ assert(false);
+ *failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (RegTypeIsUninitReference(check_type)) {
+ LOG(ERROR) << "VFY: uninitialized ref not expected as reg check";
+ *failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ /* make sure src_type is initialized reference or always-NULL */
+ if (!RegTypeIsReference(src_type)) {
+ LOG(ERROR) << "VFY: register1 v" << vsrc << " type " << src_type
+ << ", wanted ref";
+ *failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ if (RegTypeIsUninitReference(src_type)) {
+ LOG(ERROR) << "VFY: register1 v" << vsrc << " holds uninitialized ref";
+ *failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ /* if the register isn't Zero, make sure it's an instance of check */
+ if (src_type != kRegTypeZero) {
+ Class* src_class = RegTypeInitializedReferenceToClass(src_type);
+ Class* check_class = RegTypeInitializedReferenceToClass(check_type);
+ assert(src_class != NULL);
+ assert(check_class != NULL);
+
+ if (!check_class->IsAssignableFrom(src_class)) {
+ LOG(ERROR) << "VFY: " << src_class->GetDescriptor()->ToModifiedUtf8()
+ << " is not instance of "
+ << check_class->GetDescriptor()->ToModifiedUtf8();
+ *failure = VERIFY_ERROR_GENERIC;
+ }
+ }
break;
}
+}
- switch (argumentC) {
- case Instruction::kVerifyRegC:
- result &= CheckRegisterIndex(code_item, vC);
- break;
- case Instruction::kVerifyRegCField:
- result &= CheckFieldIndex(dex_file, vC);
- break;
- case Instruction::kVerifyRegCNewArray:
- result &= CheckNewArray(dex_file, vC);
- break;
- case Instruction::kVerifyRegCType:
- result &= CheckTypeIndex(dex_file, vC);
- break;
- case Instruction::kVerifyRegCWide:
- result &= CheckWideRegisterIndex(code_item, vC);
- break;
+void DexVerifier::SetResultRegisterType(RegisterLine* register_line,
+ const int insn_reg_count, RegType new_type) {
+ SetRegisterType(register_line, RESULT_REGISTER(insn_reg_count), new_type);
+}
+
+void DexVerifier::MarkRefsAsInitialized(RegisterLine* register_line,
+ int insn_reg_count, UninitInstanceMap* uninit_map, RegType uninit_type,
+ VerifyError* failure) {
+ RegType* insn_regs = register_line->reg_types_;
+ Class* klass = GetUninitInstance(uninit_map,
+ RegTypeToUninitIndex(uninit_type));
+
+ if (klass == NULL) {
+ LOG(ERROR) << "VFY: unable to find type=" << std::hex << uninit_type
+ << std::dec << " (idx=" << RegTypeToUninitIndex(uninit_type)
+ << ")";
+ *failure = VERIFY_ERROR_GENERIC;
+ return;
}
- switch (extra_flags) {
- case Instruction::kVerifyArrayData:
- result &= CheckArrayData(code_item, code_offset);
- break;
- case Instruction::kVerifyBranchTarget:
- result &= CheckBranchTarget(code_item, insn_flags, code_offset);
- break;
- case Instruction::kVerifySwitchTargets:
- result &= CheckSwitchTargets(code_item, insn_flags, code_offset);
- break;
- case Instruction::kVerifyVarArg:
- result &= CheckVarArgRegs(code_item, vA, arg);
- break;
- case Instruction::kVerifyVarArgRange:
- result &= CheckVarArgRangeRegs(code_item, vA, vC);
- break;
- case Instruction::kVerifyError:
- LOG(ERROR) << "VFY: unexpected opcode " << (int) opcode;
- result = false;
- break;
+ RegType init_type = RegTypeFromClass(klass);
+ int changed = 0;
+ for (int i = 0; i < insn_reg_count; i++) {
+ if (insn_regs[i] == uninit_type) {
+ insn_regs[i] = init_type;
+ changed++;
+ }
+ }
+ assert(changed > 0);
+
+ return;
+}
+
+void DexVerifier::MarkUninitRefsAsInvalid(RegisterLine* register_line,
+ int insn_reg_count, UninitInstanceMap* uninit_map, RegType uninit_type) {
+ RegType* insn_regs = register_line->reg_types_;
+
+ for (int i = 0; i < insn_reg_count; i++) {
+ if (insn_regs[i] == uninit_type) {
+ insn_regs[i] = kRegTypeConflict;
+ if (register_line->monitor_entries_ != NULL)
+ register_line->monitor_entries_[i] = 0;
+ }
+ }
+}
+
+void DexVerifier::CopyRegister1(RegisterLine* register_line, uint32_t vdst,
+ uint32_t vsrc, TypeCategory cat, VerifyError* failure) {
+ assert(cat == kTypeCategory1nr || cat == kTypeCategoryRef);
+ RegType type = GetRegisterType(register_line, vsrc);
+ CheckTypeCategory(type, cat, failure);
+ if (*failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: copy1 v" << vdst << "<-v" << vsrc << " type=" << type
+ << " cat=" << (int) cat;
+ } else {
+ SetRegisterType(register_line, vdst, type);
+ if (cat == kTypeCategoryRef && register_line->monitor_entries_ != NULL) {
+ register_line->monitor_entries_[vdst] =
+ register_line->monitor_entries_[vsrc];
+ }
+ }
+}
+
+void DexVerifier::CopyRegister2(RegisterLine* register_line, uint32_t vdst,
+ uint32_t vsrc, VerifyError* failure) {
+ RegType type_l = GetRegisterType(register_line, vsrc);
+ RegType type_h = GetRegisterType(register_line, vsrc + 1);
+
+ CheckTypeCategory(type_l, kTypeCategory2, failure);
+ CheckWidePair(type_l, type_h, failure);
+ if (*failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: copy2 v" << vdst << "<-v" << vsrc << " type=" << type_l
+ << "/" << type_h;
+ } else {
+ SetRegisterType(register_line, vdst, type_l);
+ }
+}
+
+void DexVerifier::CopyResultRegister1(RegisterLine* register_line,
+ const int insn_reg_count, uint32_t vdst, TypeCategory cat,
+ VerifyError* failure) {
+ assert(vdst < (uint32_t) insn_reg_count);
+
+ uint32_t vsrc = RESULT_REGISTER(insn_reg_count);
+ RegType type = GetRegisterType(register_line, vsrc);
+ CheckTypeCategory(type, cat, failure);
+ if (*failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: copyRes1 v" << vdst << "<-v" << vsrc << " cat="
+ << (int) cat << " type=" << type;
+ } else {
+ SetRegisterType(register_line, vdst, type);
+ SetRegisterType(register_line, vsrc, kRegTypeUnknown);
+ }
+}
+
+/*
+ * Implement "move-result-wide". Copy the category-2 value from the result
+ * register to another register, and reset the result register.
+ */
+void DexVerifier::CopyResultRegister2(RegisterLine* register_line,
+ const int insn_reg_count, uint32_t vdst, VerifyError* failure) {
+ assert(vdst < (uint32_t) insn_reg_count);
+
+ uint32_t vsrc = RESULT_REGISTER(insn_reg_count);
+ RegType type_l = GetRegisterType(register_line, vsrc);
+ RegType type_h = GetRegisterType(register_line, vsrc + 1);
+ CheckTypeCategory(type_l, kTypeCategory2, failure);
+ CheckWidePair(type_l, type_h, failure);
+ if (*failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: copyRes2 v" << vdst << "<-v" << vsrc << " type="
+ << type_l << "/" << type_h;
+ } else {
+ SetRegisterType(register_line, vdst, type_l);
+ SetRegisterType(register_line, vsrc, kRegTypeUnknown);
+ SetRegisterType(register_line, vsrc + 1, kRegTypeUnknown);
+ }
+}
+
+int DexVerifier::GetClassDepth(Class* klass) {
+ int depth = 0;
+ while (klass->GetSuperClass() != NULL) {
+ klass = klass->GetSuperClass();
+ depth++;
+ }
+ return depth;
+}
+
+Class* DexVerifier::DigForSuperclass(Class* c1, Class* c2) {
+ int depth1, depth2;
+
+ depth1 = GetClassDepth(c1);
+ depth2 = GetClassDepth(c2);
+
+ /* pull the deepest one up */
+ if (depth1 > depth2) {
+ while (depth1 > depth2) {
+ c1 = c1->GetSuperClass();
+ depth1--;
+ }
+ } else {
+ while (depth2 > depth1) {
+ c2 = c2->GetSuperClass();
+ depth2--;
+ }
}
+ /* walk up in lock-step */
+ while (c1 != c2) {
+ c1 = c1->GetSuperClass();
+ c2 = c2->GetSuperClass();
+
+ assert(c1 != NULL && c2 != NULL);
+ }
+
+ return c1;
+}
+
+Class* DexVerifier::FindCommonArraySuperclass(Class* c1, Class* c2) {
+ Class* array_class = NULL;
+ Class* common_elem;
+ int array_dim1, array_dim2;
+ int i, num_dims;
+ bool has_primitive = false;
+
+ array_dim1 = c1->GetArrayRank();
+ array_dim2 = c2->GetArrayRank();
+ assert(c1->GetArrayRank() > 0);
+ assert(c2->GetArrayRank() > 0);
+
+ if (c1->GetComponentType()->IsPrimitive()) {
+ array_dim1--;
+ has_primitive = true;
+ }
+ if (c2->GetComponentType()->IsPrimitive()) {
+ array_dim2--;
+ has_primitive = true;
+ }
+
+ if (!has_primitive && array_dim1 == array_dim2) {
+ /*
+ * Two arrays of reference types with equal dimensions. Try to
+ * find a good match.
+ */
+ common_elem = FindCommonSuperclass(c1->GetComponentType(),
+ c2->GetComponentType());
+ num_dims = array_dim1;
+ } else {
+ /*
+ * Mismatched array depths and/or array(s) of primitives. We want
+ * Object, or an Object array with appropriate dimensions.
+ *
+ * We initialize array_class to Object here, because it's possible
+ * for us to set num_dims=0.
+ */
+ if (array_dim1 < array_dim2)
+ num_dims = array_dim1;
+ else
+ num_dims = array_dim2;
+ array_class = common_elem = c1->GetSuperClass(); // == java.lang.Object
+ }
+
+ /*
+ * Find an appropriately-dimensioned array class. This is easiest
+ * to do iteratively, using the array class found by the current round
+ * as the element type for the next round.
+ */
+ for (i = 0; i < num_dims; i++) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const ClassLoader* class_loader = c1->GetClassLoader();
+ std::string descriptor = "[" +
+ common_elem->GetDescriptor()->ToModifiedUtf8();
+ array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+ common_elem = array_class;
+ }
+ assert(array_class != NULL);
+
+ return array_class;
+}
+
+Class* DexVerifier::FindCommonSuperclass(Class* c1, Class* c2) {
+ assert(!c1->IsPrimitive() && !c2->IsPrimitive());
+
+ if (c1 == c2)
+ return c1;
+
+ if (c1->IsInterface() && c2->Implements(c1)) {
+ return c1;
+ }
+ if (c2->IsInterface() && c1->Implements(c2)) {
+ return c2;
+ }
+ if (c1->IsArrayClass() && c2->IsArrayClass()) {
+ return FindCommonArraySuperclass(c1, c2);
+ }
+
+ return DigForSuperclass(c1, c2);
+}
+
+DexVerifier::RegType DexVerifier::MergeTypes(RegType type1, RegType type2,
+ bool* changed) {
+ RegType result;
+
+ /*
+ * Check for trivial case so we don't have to hit memory.
+ */
+ if (type1 == type2)
+ return type1;
+
+ /*
+ * Use the table if we can, and reject any attempts to merge something
+ * from the table with a reference type.
+ *
+ * Uninitialized references are composed of the enum ORed with an
+ * index value. The uninitialized table entry at index zero *will*
+ * show up as a simple kRegTypeUninit value. Since this cannot be
+ * merged with anything but itself, the rules do the right thing.
+ */
+ if (type1 < kRegTypeMAX) {
+ if (type2 < kRegTypeMAX) {
+ result = merge_table_[type1][type2];
+ } else {
+ /* simple + reference == conflict, usually */
+ if (type1 == kRegTypeZero)
+ result = type2;
+ else
+ result = kRegTypeConflict;
+ }
+ } else {
+ if (type2 < kRegTypeMAX) {
+ /* reference + simple == conflict, usually */
+ if (type2 == kRegTypeZero)
+ result = type1;
+ else
+ result = kRegTypeConflict;
+ } else {
+ /* merging two references */
+ if (RegTypeIsUninitReference(type1) ||
+ RegTypeIsUninitReference(type2))
+ {
+ /* can't merge uninit with anything but self */
+ result = kRegTypeConflict;
+ } else {
+ Class* klass1 = RegTypeInitializedReferenceToClass(type1);
+ Class* klass2 = RegTypeInitializedReferenceToClass(type2);
+ Class* merged_class = FindCommonSuperclass(klass1, klass2);
+ assert(merged_class != NULL);
+ result = RegTypeFromClass(merged_class);
+ }
+ }
+ }
+
+ if (result != type1)
+ *changed = true;
return result;
-};
+}
+
+DexVerifier::MonitorEntries DexVerifier::MergeMonitorEntries(
+ MonitorEntries ents1, MonitorEntries ents2, bool* changed) {
+ MonitorEntries result = ents1 & ents2;
+ if (result != ents1)
+ *changed = true;
+ return result;
+}
+
+bool DexVerifier::UpdateRegisters(InsnFlags* insn_flags,
+ RegisterTable* reg_table, int next_insn, const RegisterLine* work_line) {
+ const size_t insn_reg_count_plus = reg_table->insn_reg_count_plus_;
+ assert(work_line != NULL);
+ const RegType* work_regs = work_line->reg_types_;
+
+ if (!InsnIsVisitedOrChanged(insn_flags, next_insn)) {
+ /*
+ * We haven't processed this instruction before, and we haven't
+ * touched the registers here, so there's nothing to "merge". Copy
+ * the registers over and mark it as changed. (This is the only
+ * way a register can transition out of "unknown", so this is not
+ * just an optimization.)
+ */
+ CopyLineToTable(reg_table, next_insn, work_line);
+ InsnSetChanged(insn_flags, next_insn, true);
+ } else {
+ /* Merge registers, set Changed only if different */
+ RegisterLine* target_line = GetRegisterLine(reg_table, next_insn);
+ RegType* target_regs = target_line->reg_types_;
+ MonitorEntries* work_mon_ents = work_line->monitor_entries_;
+ MonitorEntries* target_mon_ents = target_line->monitor_entries_;
+ bool changed = false;
+ unsigned int idx;
+
+ assert(target_regs != NULL);
+ if (target_mon_ents != NULL) {
+ /* Monitor stacks must be identical. */
+ if (target_line->monitor_stack_top_ != work_line->monitor_stack_top_) {
+ LOG(ERROR) << "VFY: mismatched stack depth "
+ << target_line->monitor_stack_top_ << " vs. "
+ << work_line->monitor_stack_top_ << " at 0x"
+ << std::hex << next_insn << std::dec;
+ return false;
+ }
+ if (memcmp(target_line->monitor_stack_, work_line->monitor_stack_,
+ target_line->monitor_stack_top_ * sizeof(uint32_t)) != 0) {
+ LOG(ERROR) << "VFY: mismatched monitor stacks at 0x" << std::hex
+ << next_insn << std::dec;
+ return false;
+ }
+ }
+
+ for (idx = 0; idx < insn_reg_count_plus; idx++) {
+ target_regs[idx] = MergeTypes(target_regs[idx], work_regs[idx], &changed);
+
+ if (target_mon_ents != NULL) {
+ target_mon_ents[idx] = MergeMonitorEntries(target_mon_ents[idx],
+ work_mon_ents[idx], &changed);
+ }
+ }
+
+ if (changed) {
+ InsnSetChanged(insn_flags, next_insn, true);
+ }
+ }
+
+ return true;
+}
+
+bool DexVerifier::CanConvertTo1nr(RegType src_type, RegType check_type) {
+ static const char conv_tab[kRegType1nrEND - kRegType1nrSTART + 1]
+ [kRegType1nrEND - kRegType1nrSTART + 1] =
+ {
+ /* chk: 0 1 Z y Y h H c i b B s S C I F */
+ { /*0*/ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ { /*1*/ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ { /*Z*/ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
+ { /*y*/ 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ { /*Y*/ 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1 },
+ { /*h*/ 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1 },
+ { /*H*/ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1 },
+ { /*c*/ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1 },
+ { /*i*/ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1 },
+ { /*b*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0 },
+ { /*B*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0 },
+ { /*s*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 },
+ { /*S*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 },
+ { /*C*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 },
+ { /*I*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
+ { /*F*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+ };
+
+ assert(check_type >= kRegType1nrSTART && check_type <= kRegType1nrEND);
+
+ if (src_type >= kRegType1nrSTART && src_type <= kRegType1nrEND)
+ return (bool) conv_tab[src_type - kRegType1nrSTART]
+ [check_type - kRegType1nrSTART];
+
+ return false;
+}
+
+bool DexVerifier::CanConvertTo2(RegType src_type, RegType check_type) {
+ return ((src_type == kRegTypeConstLo || src_type == check_type) &&
+ (check_type == kRegTypeLongLo || check_type == kRegTypeDoubleLo));
+}
+
+DexVerifier::RegType DexVerifier::PrimitiveTypeToRegType(
+ Class::PrimitiveType prim_type) {
+ switch (prim_type) {
+ case Class::kPrimBoolean: return kRegTypeBoolean;
+ case Class::kPrimByte: return kRegTypeByte;
+ case Class::kPrimShort: return kRegTypeShort;
+ case Class::kPrimChar: return kRegTypeChar;
+ case Class::kPrimInt: return kRegTypeInteger;
+ case Class::kPrimLong: return kRegTypeLongLo;
+ case Class::kPrimFloat: return kRegTypeFloat;
+ case Class::kPrimDouble: return kRegTypeDoubleLo;
+ case Class::kPrimVoid:
+ default: {
+ return kRegTypeUnknown;
+ }
+ }
+}
+
+DexVerifier::RegType DexVerifier::ConstTypeToRegType(RegType const_type) {
+ switch (const_type) {
+ case kRegTypeConstPosByte: return kRegTypePosByte;
+ case kRegTypeConstByte: return kRegTypeByte;
+ case kRegTypeConstPosShort: return kRegTypePosShort;
+ case kRegTypeConstShort: return kRegTypeShort;
+ case kRegTypeConstChar: return kRegTypeChar;
+ case kRegTypeConstInteger: return kRegTypeInteger;
+ default: {
+ return const_type;
+ }
+ }
+}
+
+char DexVerifier::DetermineCat1Const(int32_t value) {
+ if (value < -32768)
+ return kRegTypeConstInteger;
+ else if (value < -128)
+ return kRegTypeConstShort;
+ else if (value < 0)
+ return kRegTypeConstByte;
+ else if (value == 0)
+ return kRegTypeZero;
+ else if (value == 1)
+ return kRegTypeOne;
+ else if (value < 128)
+ return kRegTypeConstPosByte;
+ else if (value < 32768)
+ return kRegTypeConstPosShort;
+ else if (value < 65536)
+ return kRegTypeConstChar;
+ else
+ return kRegTypeConstInteger;
+}
+
+void DexVerifier::CheckFinalFieldAccess(const Method* method,
+ const Field* field, VerifyError* failure) {
+ if (!field->IsFinal())
+ return;
+
+ /* make sure we're in the same class */
+ if (method->GetDeclaringClass() != field->GetDeclaringClass()) {
+ LOG(ERROR) << "VFY: can't modify final field "
+ << field->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+ << "." << field->GetName()->ToModifiedUtf8();
+ *failure = VERIFY_ERROR_ACCESS_FIELD;
+ return;
+ }
+}
+
+void DexVerifier::CheckArrayIndexType(const Method* method, RegType reg_type,
+ VerifyError* failure) {
+ if (*failure == VERIFY_ERROR_NONE) {
+ /*
+ * The 1nr types are interchangeable at this level. We could
+ * do something special if we can definitively identify it as a
+ * float, but there's no real value in doing so.
+ */
+ CheckTypeCategory(reg_type, kTypeCategory1nr, failure);
+ if (*failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "Invalid reg type for array index (" << reg_type << ")";
+ }
+ }
+}
+
+bool DexVerifier::CheckConstructorReturn(const Method* method,
+ const RegisterLine* register_line, const int insn_reg_count) {
+ const RegType* insn_regs = register_line->reg_types_;
+
+ if (!IsInitMethod(method))
+ return true;
+
+ RegType uninit_this = RegTypeFromUninitIndex(kUninitThisArgSlot);
+
+ for (int i = 0; i < insn_reg_count; i++) {
+ if (insn_regs[i] == uninit_this) {
+ LOG(ERROR) << "VFY: <init> returning without calling superclass init";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DexVerifier::CheckMoveException(const uint16_t* insns, int insn_idx) {
+ if ((insns[insn_idx] & 0xff) == Instruction::MOVE_EXCEPTION) {
+ LOG(ERROR) << "VFY: invalid use of move-exception";
+ return false;
+ }
+ return true;
+}
+
+void DexVerifier::CheckTypeCategory(RegType type, TypeCategory cat,
+ VerifyError* failure) {
+ switch (cat) {
+ case kTypeCategory1nr:
+ switch (type) {
+ case kRegTypeZero:
+ case kRegTypeOne:
+ case kRegTypeBoolean:
+ case kRegTypeConstPosByte:
+ case kRegTypeConstByte:
+ case kRegTypeConstPosShort:
+ case kRegTypeConstShort:
+ case kRegTypeConstChar:
+ case kRegTypeConstInteger:
+ case kRegTypePosByte:
+ case kRegTypeByte:
+ case kRegTypePosShort:
+ case kRegTypeShort:
+ case kRegTypeChar:
+ case kRegTypeInteger:
+ case kRegTypeFloat:
+ break;
+ default:
+ *failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ break;
+ case kTypeCategory2:
+ switch (type) {
+ case kRegTypeConstLo:
+ case kRegTypeLongLo:
+ case kRegTypeDoubleLo:
+ break;
+ default:
+ *failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+ break;
+ case kTypeCategoryRef:
+ if (type != kRegTypeZero && !RegTypeIsReference(type))
+ *failure = VERIFY_ERROR_GENERIC;
+ break;
+ default:
+ assert(false);
+ *failure = VERIFY_ERROR_GENERIC;
+ break;
+ }
+}
+
+void DexVerifier::CheckWidePair(RegType type_l, RegType type_h,
+ VerifyError* failure) {
+ if ((type_h != type_l + 1))
+ *failure = VERIFY_ERROR_GENERIC;
+}
+
+void DexVerifier::CheckUnop(RegisterLine* register_line,
+ Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+ RegType src_type, VerifyError* failure) {
+ VerifyRegisterType(register_line, dec_insn->vB_, src_type, failure);
+ SetRegisterType(register_line, dec_insn->vA_, dst_type);
+}
+
+bool DexVerifier::UpcastBooleanOp(RegisterLine* register_line, uint32_t reg1,
+ uint32_t reg2) {
+ RegType type1, type2;
+
+ type1 = GetRegisterType(register_line, reg1);
+ type2 = GetRegisterType(register_line, reg2);
+
+ if ((type1 == kRegTypeBoolean || type1 == kRegTypeZero || type1 == kRegTypeOne) &&
+ (type2 == kRegTypeBoolean || type2 == kRegTypeZero || type2 == kRegTypeOne)) {
+ return true;
+ }
+ return false;
+}
+
+void DexVerifier::CheckLitop(RegisterLine* register_line,
+ Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+ RegType src_type, bool check_boolean_op, VerifyError* failure) {
+ VerifyRegisterType(register_line, dec_insn->vB_, src_type, failure);
+
+ if ((*failure == VERIFY_ERROR_NONE) && check_boolean_op) {
+ assert(dst_type == kRegTypeInteger);
+
+ /* check vB with the call, then check the constant manually */
+ if (UpcastBooleanOp(register_line, dec_insn->vB_, dec_insn->vB_)
+ && (dec_insn->vC_ == 0 || dec_insn->vC_ == 1)) {
+ dst_type = kRegTypeBoolean;
+ }
+ }
+
+ SetRegisterType(register_line, dec_insn->vA_, dst_type);
+}
+
+void DexVerifier::CheckBinop(RegisterLine* register_line,
+ Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+ RegType src_type1, RegType src_type2, bool check_boolean_op,
+ VerifyError* failure) {
+ VerifyRegisterType(register_line, dec_insn->vB_, src_type1, failure);
+ VerifyRegisterType(register_line, dec_insn->vC_, src_type2, failure);
+
+ if ((*failure == VERIFY_ERROR_NONE) && check_boolean_op) {
+ assert(dst_type == kRegTypeInteger);
+ if (UpcastBooleanOp(register_line, dec_insn->vB_, dec_insn->vC_))
+ dst_type = kRegTypeBoolean;
+ }
+
+ SetRegisterType(register_line, dec_insn->vA_, dst_type);
+}
+
+void DexVerifier::CheckBinop2addr(RegisterLine* register_line,
+ Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+ RegType src_type1, RegType src_type2, bool check_boolean_op,
+ VerifyError* failure) {
+ VerifyRegisterType(register_line, dec_insn->vA_, src_type1, failure);
+ VerifyRegisterType(register_line, dec_insn->vB_, src_type2, failure);
+
+ if ((*failure == VERIFY_ERROR_NONE) && check_boolean_op) {
+ assert(dst_type == kRegTypeInteger);
+ if (UpcastBooleanOp(register_line, dec_insn->vA_, dec_insn->vB_))
+ dst_type = kRegTypeBoolean;
+ }
+
+ SetRegisterType(register_line, dec_insn->vA_, dst_type);
+}
+
+DexVerifier::RegType DexVerifier::AdjustForRightShift(
+ RegisterLine* register_line, int reg, unsigned int shift_count,
+ bool is_unsigned_shift, VerifyError* failure) {
+ RegType src_type = GetRegisterType(register_line, reg);
+ RegType new_type;
+
+ /* convert const derived types to their actual types */
+ src_type = ConstTypeToRegType(src_type);
+
+ /* no-op */
+ if (shift_count == 0)
+ return src_type;
+
+ /* safe defaults */
+ if (is_unsigned_shift)
+ new_type = kRegTypeInteger;
+ else
+ new_type = src_type;
+
+ if (shift_count >= 32) {
+ LOG(ERROR) << "Got unexpectedly large shift count " << shift_count;
+ /* fail? */
+ return new_type;
+ }
+
+ switch (src_type) {
+ case kRegTypeInteger: /* 32-bit signed value */
+ if (is_unsigned_shift) {
+ if (shift_count > 24)
+ new_type = kRegTypePosByte;
+ else if (shift_count >= 16)
+ new_type = kRegTypeChar;
+ } else {
+ if (shift_count >= 24)
+ new_type = kRegTypeByte;
+ else if (shift_count >= 16)
+ new_type = kRegTypeShort;
+ }
+ break;
+ case kRegTypeShort: /* 16-bit signed value */
+ if (is_unsigned_shift) {
+ /* default (kRegTypeInteger) is correct */
+ } else {
+ if (shift_count >= 8)
+ new_type = kRegTypeByte;
+ }
+ break;
+ case kRegTypePosShort: /* 15-bit unsigned value */
+ if (shift_count >= 8)
+ new_type = kRegTypePosByte;
+ break;
+ case kRegTypeChar: /* 16-bit unsigned value */
+ if (shift_count > 8)
+ new_type = kRegTypePosByte;
+ break;
+ case kRegTypeByte: /* 8-bit signed value */
+ /* defaults (u=kRegTypeInteger / s=src_type) are correct */
+ break;
+ case kRegTypePosByte: /* 7-bit unsigned value */
+ /* always use new_type=src_type */
+ new_type = src_type;
+ break;
+ case kRegTypeZero: /* 1-bit unsigned value */
+ case kRegTypeOne:
+ case kRegTypeBoolean:
+ /* unnecessary? */
+ new_type = kRegTypeZero;
+ break;
+ default:
+ /* long, double, references; shouldn't be here! */
+ assert(false);
+ break;
+ }
+
+ return new_type;
+}
+
+void DexVerifier::VerifyFilledNewArrayRegs(const Method* method,
+ RegisterLine* register_line,
+ const Instruction::DecodedInstruction* dec_insn, Class* res_class,
+ bool is_range, VerifyError* failure) {
+ uint32_t arg_count = dec_insn->vA_;
+ RegType expected_type;
+ Class::PrimitiveType elem_type;
+ unsigned int ui;
+
+ assert(res_class->IsArrayClass());
+ elem_type = res_class->GetComponentType()->GetPrimitiveType();
+ if (elem_type == Class::kPrimNot) {
+ expected_type = RegTypeFromClass(res_class->GetComponentType());
+ } else {
+ expected_type = PrimitiveTypeToRegType(elem_type);
+ }
+
+ /*
+ * Verify each register. If "arg_count" is bad, VerifyRegisterType()
+ * will run off the end of the list and fail. It's legal, if silly,
+ * for arg_count to be zero.
+ */
+ for (ui = 0; ui < arg_count; ui++) {
+ uint32_t get_reg;
+
+ if (is_range)
+ get_reg = dec_insn->vC_ + ui;
+ else
+ get_reg = dec_insn->arg_[ui];
+
+ VerifyRegisterType(register_line, get_reg, expected_type, failure);
+ if (*failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: filled-new-array arg " << ui << "(" << get_reg
+ << ") not valid";
+ return;
+ }
+ }
+}
+
+bool DexVerifier::IsCorrectInvokeKind(MethodType method_type,
+ Method* res_method) {
+ switch (method_type) {
+ case METHOD_DIRECT:
+ return res_method->IsDirect();
+ case METHOD_STATIC:
+ return res_method->IsStatic();
+ case METHOD_VIRTUAL:
+ case METHOD_INTERFACE:
+ return !res_method->IsDirect();
+ default:
+ return false;
+ }
+}
+
+Method* DexVerifier::VerifyInvocationArgs(VerifierData* vdata,
+ RegisterLine* register_line, const int insn_reg_count,
+ const Instruction::DecodedInstruction* dec_insn, MethodType method_type,
+ bool is_range, bool is_super, VerifyError* failure) {
+ Method* method = vdata->method_;
+ const DexFile* dex_file = vdata->dex_file_;
+ const DexFile::CodeItem* code_item = vdata->code_item_;
+ UninitInstanceMap* uninit_map = vdata->uninit_map_;
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
+ const ClassLoader* class_loader =
+ method->GetDeclaringClass()->GetClassLoader();
+
+ Method* res_method;
+ std::string sig;
+ size_t sig_offset;
+ int expected_args;
+ int actual_args;
+
+ /*
+ * Resolve the method. This could be an abstract or concrete method
+ * depending on what sort of call we're making.
+ */
+ res_method = class_linker->ResolveMethod(*dex_file, dec_insn->vB_, dex_cache,
+ class_loader, (method_type == METHOD_DIRECT || method_type == METHOD_STATIC));
+
+ /* Scan all implemented interfaces for the method */
+ if (method_type == METHOD_INTERFACE && res_method == NULL) {
+
+ }
+
+ if (res_method == NULL) {
+ /* failed; print a meaningful failure message */
+ //const DexFile::MethodId method_id = dex_file->GetMethodId(dec_insn->vB_);
+ //const char* method_name = dex_file->dexStringById(method_id.name_idx_);
+ //const char* class_name = dex_file->dexStringByTypeIdx(method_id.class_idx_);
+
+ //LOG(ERROR) << "VFY: unable to resolve " << (int) method_type << " method "
+ //<< dec_insn->vB_ << ": " << class_name << "." << method_name;
+ LOG(ERROR) << "VFY: unable to resolve called method";
+ *failure = VERIFY_ERROR_NO_METHOD;
+ return NULL;
+ }
+ Class* res_class = res_method->GetDeclaringClass();
+
+ /*
+ * Only time you can explicitly call a method starting with '<' is when
+ * making a "direct" invocation on "<init>". There are additional
+ * restrictions but we don't enforce them here.
+ */
+ if (res_method->GetName()->Equals("<init>")) {
+ if (method_type != METHOD_DIRECT || !IsInitMethod(res_method)) {
+ LOG(ERROR) << "VFY: invalid call to "
+ << res_class->GetDescriptor()->ToModifiedUtf8()
+ << "." << res_method->GetName();
+ goto bad_sig;
+ }
+ }
+
+ /*
+ * See if the method type implied by the invoke instruction matches the
+ * access flags for the target method.
+ */
+ if (!IsCorrectInvokeKind(method_type, res_method)) {
+ LOG(ERROR) << "VFY: invoke type does not match method type of "
+ << res_class->GetDescriptor()->ToModifiedUtf8()
+ << "." << res_method->GetName()->ToModifiedUtf8();
+
+ *failure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+
+ /*
+ * If we're using invoke-super(method), make sure that the executing
+ * method's class' superclass has a vtable entry for the target method.
+ */
+ if (is_super) {
+ assert(method_type == METHOD_VIRTUAL);
+ Class* super = method->GetDeclaringClass()->GetSuperClass();
+ if (super == NULL || res_method->GetMethodIndex() > super->GetVTable()->GetLength()) {
+ if (super == NULL) {
+ LOG(ERROR) << "VFY: invalid invoke-super from "
+ << method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+ << "." << method->GetName()->ToModifiedUtf8() << " to super -."
+ << res_method->GetName()->ToModifiedUtf8()
+ << " " << res_method->GetSignature()->ToModifiedUtf8();
+ } else {
+ LOG(ERROR) << "VFY: invalid invoke-super from "
+ << method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+ << "." << method->GetName()->ToModifiedUtf8() << " to super "
+ << super->GetDescriptor()->ToModifiedUtf8()
+ << "." << res_method->GetName()->ToModifiedUtf8()
+ << " " << res_method->GetSignature()->ToModifiedUtf8();
+ }
+ *failure = VERIFY_ERROR_NO_METHOD;
+ return NULL;
+ }
+ }
+
+ /*
+ * We use vAA as our expected arg count, rather than res_method->insSize,
+ * because we need to match the call to the signature. Also, we might
+ * might be calling through an abstract method definition (which doesn't
+ * have register count values).
+ */
+ expected_args = dec_insn->vA_;
+ actual_args = 0;
+
+ /* caught by static verifier */
+ assert(is_range || expected_args <= 5);
+
+ if (expected_args > code_item->outs_size_) {
+ LOG(ERROR) << "VFY: invalid arg count (" << expected_args
+ << ") exceeds outsSize (" << code_item->outs_size_ << ")";
+ *failure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+
+ sig = res_method->GetSignature()->ToModifiedUtf8();
+ if (sig[0] != '(') {
+ LOG(ERROR) << "VFY: descriptor doesn't start with '(': " << sig;
+ goto bad_sig;
+ }
+
+ /*
+ * Check the "this" argument, which must be an instance of the class
+ * that declared the method. For an interface class, we don't do the
+ * full interface merge, so we can't do a rigorous check here (which
+ * is okay since we have to do it at runtime).
+ */
+ if (!res_method->IsStatic()) {
+ Class* actual_this_ref;
+ RegType actual_arg_type;
+
+ actual_arg_type = GetInvocationThis(register_line, dec_insn, failure);
+ if (*failure != VERIFY_ERROR_NONE)
+ return NULL;
+
+ if (RegTypeIsUninitReference(actual_arg_type) &&
+ !res_method->GetName()->Equals("<init>")) {
+ LOG(ERROR) << "VFY: 'this' arg must be initialized";
+ *failure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+ if (method_type != METHOD_INTERFACE && actual_arg_type != kRegTypeZero) {
+ actual_this_ref = RegTypeReferenceToClass(actual_arg_type, uninit_map);
+ if (!res_method->GetDeclaringClass()->IsAssignableFrom(actual_this_ref)) {
+ LOG(ERROR) << "VFY: 'this' arg '"
+ << actual_this_ref->GetDescriptor()->ToModifiedUtf8()
+ << "' not instance of '"
+ << res_method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+ << "'";
+ *failure = VERIFY_ERROR_GENERIC;
+ return NULL;
+ }
+ }
+ actual_args++;
+ }
+
+ /*
+ * Process the target method's signature. This signature may or may not
+ * have been verified, so we can't assume it's properly formed.
+ */
+ for (sig_offset = 1; sig_offset < sig.size(); sig_offset++) {
+ //while (*sig != '\0' && *sig != ')') {
+ if (sig[sig_offset] == ')')
+ break;
+
+ if (actual_args >= expected_args) {
+ LOG(ERROR) << "VFY: expected " << expected_args << " args, found more ("
+ << sig.substr(sig_offset) << ")";
+ goto bad_sig;
+ }
+
+ uint32_t get_reg;
+ if (is_range)
+ get_reg = dec_insn->vC_ + actual_args;
+ else
+ get_reg = dec_insn->arg_[actual_args];
+
+ switch (sig[sig_offset]) {
+ case 'L':
+ {
+ Class* klass = LookupSignatureClass(method, sig.substr(sig_offset),
+ failure);
+ if (*failure != VERIFY_ERROR_NONE)
+ goto bad_sig;
+ VerifyRegisterType(register_line, get_reg, RegTypeFromClass(klass),
+ failure);
+ if (*failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: bad arg " << actual_args << " (into "
+ << klass->GetDescriptor()->ToModifiedUtf8() << ")";
+ goto bad_sig;
+ }
+ sig_offset += sig.substr(sig_offset).find(';');
+ }
+ actual_args++;
+ break;
+ case '[':
+ {
+ Class* klass = LookupSignatureArrayClass(method,
+ sig.substr(sig_offset), failure);
+ if (*failure != VERIFY_ERROR_NONE)
+ goto bad_sig;
+ VerifyRegisterType(register_line, get_reg, RegTypeFromClass(klass),
+ failure);
+ if (*failure != VERIFY_ERROR_NONE) {
+ LOG(ERROR) << "VFY: bad arg " << actual_args << " (into "
+ << klass->GetDescriptor()->ToModifiedUtf8() << ")";
+ goto bad_sig;
+ }
+ while (sig[sig_offset] == '[')
+ sig_offset++;
+ if (sig[sig_offset] == 'L')
+ sig_offset += sig.substr(sig_offset).find(';');
+ }
+ actual_args++;
+ break;
+ case 'Z':
+ VerifyRegisterType(register_line, get_reg, kRegTypeBoolean, failure);
+ actual_args++;
+ break;
+ case 'C':
+ VerifyRegisterType(register_line, get_reg, kRegTypeChar, failure);
+ actual_args++;
+ break;
+ case 'B':
+ VerifyRegisterType(register_line, get_reg, kRegTypeByte, failure);
+ actual_args++;
+ break;
+ case 'I':
+ VerifyRegisterType(register_line, get_reg, kRegTypeInteger, failure);
+ actual_args++;
+ break;
+ case 'S':
+ VerifyRegisterType(register_line, get_reg, kRegTypeShort, failure);
+ actual_args++;
+ break;
+ case 'F':
+ VerifyRegisterType(register_line, get_reg, kRegTypeFloat, failure);
+ actual_args++;
+ break;
+ case 'D':
+ VerifyRegisterType(register_line, get_reg, kRegTypeDoubleLo, failure);
+ actual_args += 2;
+ break;
+ case 'J':
+ VerifyRegisterType(register_line, get_reg, kRegTypeLongLo, failure);
+ actual_args += 2;
+ break;
+ default:
+ LOG(ERROR) << "VFY: invocation target: bad signature type char '"
+ << sig << "'";
+ goto bad_sig;
+ }
+ }
+ if (sig[sig_offset] != ')') {
+ LOG(ERROR) << "VFY: invocation target: bad signature '"
+ << res_method->GetSignature()->ToModifiedUtf8() << "'";
+ goto bad_sig;
+ }
+
+ if (actual_args != expected_args) {
+ LOG(ERROR) << "VFY: expected " << expected_args << " args, found "
+ << actual_args;
+ goto bad_sig;
+ }
+
+ return res_method;
+
+bad_sig:
+ if (res_method != NULL) {
+ LOG(ERROR) << "VFY: rejecting call to "
+ << res_method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+ << "." << res_method->GetName()->ToModifiedUtf8() << " "
+ << res_method->GetSignature()->ToModifiedUtf8();
+ }
+
+ if (*failure == VERIFY_ERROR_NONE)
+ *failure = VERIFY_ERROR_GENERIC;
+ return NULL;
+}
} // namespace art
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index e909b35..b6fbd46 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -10,31 +10,1488 @@
namespace art {
-class DexVerify {
+#define kMaxMonitorStackDepth (sizeof(MonitorEntries) * 8)
+
+/*
+ * Set this to enable dead code scanning. This is not required, but it's
+ * very useful when testing changes to the verifier (to make sure we're not
+ * skipping over stuff). The only reason not to do it is that it slightly
+ * increases the time required to perform verification.
+ */
+#ifndef NDEBUG
+# define DEAD_CODE_SCAN true
+#else
+# define DEAD_CODE_SCAN false
+#endif
+
+/*
+ * We need an extra "pseudo register" to hold the return type briefly. It
+ * can be category 1 or 2, so we need two slots.
+ */
+#define kExtraRegs 2
+#define RESULT_REGISTER(_insnRegCount) (_insnRegCount)
+
+class DexVerifier {
public:
- enum {
- kInsnFlagWidthMask = 0x0000ffff,
- kInsnFlagInTry = (1 << 16),
+ /*
+ * RegType holds information about the type of data held in a register.
+ * For most types it's a simple enum. For reference types it holds a
+ * pointer to the ClassObject, and for uninitialized references it holds
+ * an index into the UninitInstanceMap.
+ */
+ typedef uint32_t RegType;
+
+ /*
+ * A bit vector indicating which entries in the monitor stack are
+ * associated with this register. The low bit corresponds to the stack's
+ * bottom-most entry.
+ */
+ typedef uint32_t MonitorEntries;
+
+ /*
+ * InsnFlags is a 32-bit integer with the following layout:
+ * 0-15 instruction length (or 0 if this address doesn't hold an opcode)
+ * 16-31 single bit flags:
+ * InTry: in "try" block; exceptions thrown here may be caught locally
+ * BranchTarget: other instructions can branch to this instruction
+ * GcPoint: this instruction is a GC safe point
+ * Visited: verifier has examined this instruction at least once
+ * Changed: set/cleared as bytecode verifier runs
+ */
+ typedef uint32_t InsnFlags;
+
+ enum InsnFlag {
+ kInsnFlagWidthMask = 0x0000ffff,
+ kInsnFlagInTry = (1 << 16),
kInsnFlagBranchTarget = (1 << 17),
- kInsnFlagGcPoint = (1 << 18),
- kInsnFlagVisited = (1 << 30),
- kInsnFlagChanged = (1 << 31),
+ kInsnFlagGcPoint = (1 << 18),
+ kInsnFlagVisited = (1 << 30),
+ kInsnFlagChanged = (1 << 31),
};
+ /*
+ * "Direct" and "virtual" methods are stored independently. The type of call
+ * used to invoke the method determines which list we search, and whether
+ * we travel up into superclasses.
+ *
+ * (<clinit>, <init>, and methods declared "private" or "static" are stored
+ * in the "direct" list. All others are stored in the "virtual" list.)
+ */
+ enum MethodType {
+ METHOD_UNKNOWN = 0,
+ METHOD_DIRECT, // <init>, private
+ METHOD_STATIC, // static
+ METHOD_VIRTUAL, // virtual, super
+ METHOD_INTERFACE // interface
+ };
+
+ /*
+ * We don't need to store the register data for many instructions, because
+ * we either only need it at branch points (for verification) or GC points
+ * and branches (for verification + type-precise register analysis).
+ */
+ enum RegisterTrackingMode {
+ kTrackRegsBranches,
+ kTrackRegsGcPoints,
+ kTrackRegsAll,
+ };
+
+ /*
+ * Enumeration for register type values. The "hi" piece of a 64-bit value
+ * MUST immediately follow the "lo" piece in the enumeration, so we can check
+ * that hi==lo+1.
+ *
+ * Assignment of constants:
+ * [-MAXINT,-32768) : integer
+ * [-32768,-128) : short
+ * [-128,0) : byte
+ * 0 : zero
+ * 1 : one
+ * [2,128) : posbyte
+ * [128,32768) : posshort
+ * [32768,65536) : char
+ * [65536,MAXINT] : integer
+ *
+ * Allowed "implicit" widening conversions:
+ * zero -> boolean, posbyte, byte, posshort, short, char, integer, ref (null)
+ * one -> boolean, posbyte, byte, posshort, short, char, integer
+ * boolean -> posbyte, byte, posshort, short, char, integer
+ * posbyte -> posshort, short, integer, char
+ * byte -> short, integer
+ * posshort -> integer, char
+ * short -> integer
+ * char -> integer
+ *
+ * In addition, all of the above can convert to "float".
+ *
+ * We're more careful with integer values than the spec requires. The
+ * motivation is to restrict byte/char/short to the correct range of values.
+ * For example, if a method takes a byte argument, we don't want to allow
+ * the code to load the constant "1024" and pass it in.
+ */
+ enum {
+ kRegTypeUnknown = 0, /* initial state; use value=0 so calloc works */
+ kRegTypeUninit = 1, /* MUST be odd to distinguish from pointer */
+ kRegTypeConflict, /* merge clash makes this reg's type unknowable */
+
+ /*
+ * Category-1nr types. The order of these is chiseled into a couple
+ * of tables, so don't add, remove, or reorder if you can avoid it.
+ */
+#define kRegType1nrSTART kRegTypeZero
+ kRegTypeZero, /* 32-bit 0, could be Boolean, Int, Float, or Ref */
+ kRegTypeOne, /* 32-bit 1, could be Boolean, Int, Float */
+ kRegTypeBoolean, /* must be 0 or 1 */
+ kRegTypeConstPosByte, /* const derived byte, known positive */
+ kRegTypeConstByte, /* const derived byte */
+ kRegTypeConstPosShort, /* const derived short, known positive */
+ kRegTypeConstShort, /* const derived short */
+ kRegTypeConstChar, /* const derived char */
+ kRegTypeConstInteger, /* const derived integer */
+ kRegTypePosByte, /* byte, known positive (can become char) */
+ kRegTypeByte,
+ kRegTypePosShort, /* short, known positive (can become char) */
+ kRegTypeShort,
+ kRegTypeChar,
+ kRegTypeInteger,
+ kRegTypeFloat,
+#define kRegType1nrEND kRegTypeFloat
+ kRegTypeConstLo, /* const derived wide, lower half */
+ kRegTypeConstHi, /* const derived wide, upper half */
+ kRegTypeLongLo, /* lower-numbered register; endian-independent */
+ kRegTypeLongHi,
+ kRegTypeDoubleLo,
+ kRegTypeDoubleHi,
+
+ /*
+ * Enumeration max; this is used with "full" (32-bit) RegType values.
+ *
+ * Anything larger than this is a ClassObject or uninit ref. Mask off
+ * all but the low 8 bits; if you're left with kRegTypeUninit, pull
+ * the uninit index out of the high 24. Because kRegTypeUninit has an
+ * odd value, there is no risk of a particular ClassObject pointer bit
+ * pattern being confused for it (assuming our class object allocator
+ * uses word alignment).
+ */
+ kRegTypeMAX
+ };
+#define kRegTypeUninitMask 0xff
+#define kRegTypeUninitShift 8
+
+ /*
+ * Register type categories, for type checking.
+ *
+ * The spec says category 1 includes boolean, byte, char, short, int, float,
+ * reference, and returnAddress. Category 2 includes long and double.
+ *
+ * We treat object references separately, so we have "category1nr". We
+ * don't support jsr/ret, so there is no "returnAddress" type.
+ */
+ enum TypeCategory {
+ kTypeCategoryUnknown = 0,
+ kTypeCategory1nr = 1, // boolean, byte, char, short, int, float
+ kTypeCategory2 = 2, // long, double
+ kTypeCategoryRef = 3, // object reference
+ };
+
+ /* An enumeration of problems that can turn up during verification. */
+ enum VerifyError {
+ VERIFY_ERROR_NONE = 0, /* no error; must be zero */
+ VERIFY_ERROR_GENERIC, /* VerifyError */
+
+ VERIFY_ERROR_NO_CLASS, /* NoClassDefFoundError */
+ VERIFY_ERROR_NO_FIELD, /* NoSuchFieldError */
+ VERIFY_ERROR_NO_METHOD, /* NoSuchMethodError */
+ VERIFY_ERROR_ACCESS_CLASS, /* IllegalAccessError */
+ VERIFY_ERROR_ACCESS_FIELD, /* IllegalAccessError */
+ VERIFY_ERROR_ACCESS_METHOD, /* IllegalAccessError */
+ VERIFY_ERROR_CLASS_CHANGE, /* IncompatibleClassChangeError */
+ VERIFY_ERROR_INSTANTIATION, /* InstantiationError */
+ };
+
+ /*
+ * Identifies the type of reference in the instruction that generated the
+ * verify error (e.g. VERIFY_ERROR_ACCESS_CLASS could come from a method,
+ * field, or class reference).
+ *
+ * This must fit in two bits.
+ */
+ enum VerifyErrorRefType {
+ VERIFY_ERROR_REF_CLASS = 0,
+ VERIFY_ERROR_REF_FIELD = 1,
+ VERIFY_ERROR_REF_METHOD = 2,
+ };
+#define kVerifyErrorRefTypeShift 6
+
+ /*
+ * During verification, we associate one of these with every "interesting"
+ * instruction. We track the status of all registers, and (if the method
+ * has any monitor-enter instructions) maintain a stack of entered monitors
+ * (identified by code unit offset).
+ *
+ * If live-precise register maps are enabled, the "liveRegs" vector will
+ * be populated. Unlike the other lists of registers here, we do not
+ * track the liveness of the method result register (which is not visible
+ * to the GC).
+ */
+ struct RegisterLine {
+ RegType* reg_types_;
+ MonitorEntries* monitor_entries_;
+ uint32_t* monitor_stack_;
+ uint32_t monitor_stack_top_;
+
+ /* Default constructor. */
+ RegisterLine() {
+ reg_types_ = NULL;
+ monitor_entries_ = NULL;
+ monitor_stack_ = NULL;
+ monitor_stack_top_ = 0;
+ }
+
+ /* Default destructor. */
+ ~RegisterLine() {
+ delete reg_types_;
+ delete monitor_entries_;
+ delete monitor_stack_;
+ }
+
+ /* Allocate space for the fields. */
+ void Alloc(size_t size, bool track_monitors) {
+ reg_types_ = new RegType[size]();
+ if (track_monitors) {
+ monitor_entries_ = new MonitorEntries[size];
+ monitor_stack_ = new uint32_t[kMaxMonitorStackDepth];
+ }
+ }
+ };
+
+ /* Big fat collection of register data. */
+ struct RegisterTable {
+ /*
+ * Array of RegisterLine structs, one per address in the method. We only
+ * set the pointers for certain addresses, based on instruction widths
+ * and what we're trying to accomplish.
+ */
+ RegisterLine* register_lines_;
+
+ /*
+ * Number of registers we track for each instruction. This is equal
+ * to the method's declared "registersSize" plus kExtraRegs (2).
+ */
+ size_t insn_reg_count_plus_;
+
+ /* Storage for a register line we're currently working on. */
+ RegisterLine work_line_;
+
+ /* Storage for a register line we're saving for later. */
+ RegisterLine saved_line_;
+
+ /* Default constructor. */
+ RegisterTable() {
+ register_lines_ = NULL;
+ insn_reg_count_plus_ = 0;
+ }
+
+ /* Default destructor. */
+ ~RegisterTable() {
+ delete [] register_lines_;
+ }
+ };
+
+ /* Entries in the UninitInstanceMap. */
+ struct UninitInstanceMapEntry {
+ /* Code offset, or -1 for method arg ("this"). */
+ int addr_;
+
+ /* Class created at this address. */
+ Class* klass_;
+ };
+
+ /*
+ * Table that maps uninitialized instances to classes, based on the
+ * address of the new-instance instruction. One per method.
+ */
+ struct UninitInstanceMap {
+ int num_entries_;
+ UninitInstanceMapEntry* map_;
+
+ /* Basic constructor */
+ UninitInstanceMap(int num_entries) {
+ num_entries_ = num_entries;
+ map_ = new UninitInstanceMapEntry[num_entries]();
+ }
+
+ /* Default destructor */
+ ~UninitInstanceMap() {
+ delete map_;
+ }
+ };
+ #define kUninitThisArgAddr (-1)
+ #define kUninitThisArgSlot 0
+
+ /* Various bits of data used by the verifier and register map generator. */
+ struct VerifierData {
+ /* The method we're working on. */
+ Method* method_;
+
+ /* The dex file containing the method. */
+ const DexFile* dex_file_;
+
+ /* The code item containing the code for the method. */
+ const DexFile::CodeItem* code_item_;
+
+ /* Instruction widths and flags, one entry per code unit. */
+ InsnFlags* insn_flags_;
+
+ /*
+ * Uninitialized instance map, used for tracking the movement of
+ * objects that have been allocated but not initialized.
+ */
+ UninitInstanceMap* uninit_map_;
+
+ /*
+ * Array of RegisterLine structs, one entry per code unit. We only need
+ * entries for code units that hold the start of an "interesting"
+ * instruction. For register map generation, we're only interested
+ * in GC points.
+ */
+ RegisterLine* register_lines_;
+
+ /* The number of occurrences of specific opcodes. */
+ size_t new_instance_count_;
+ size_t monitor_enter_count_;
+
+ /* Basic constructor. */
+ VerifierData(Method* method, const DexFile* dex_file,
+ const DexFile::CodeItem* code_item)
+ : method_(method), dex_file_(dex_file), code_item_(code_item),
+ insn_flags_(NULL), uninit_map_(NULL), register_lines_(NULL),
+ new_instance_count_(0), monitor_enter_count_(0) { }
+ };
+
+ /*
+ * Merge result table for primitive values. The table is symmetric along
+ * the diagonal.
+ *
+ * Note that 32-bit int/float do not merge into 64-bit long/double. This
+ * is a register merge, not a widening conversion. Only the "implicit"
+ * widening within a category, e.g. byte to short, is allowed.
+ *
+ * Dalvik does not draw a distinction between int and float, but we enforce
+ * that once a value is used as int, it can't be used as float, and vice
+ * versa. We do not allow free exchange between 32-bit int/float and 64-bit
+ * long/double.
+ *
+ * Note that Uninit+Uninit=Uninit. This holds true because we only
+ * use this when the RegType value is exactly equal to kRegTypeUninit, which
+ * can only happen for the zeroeth entry in the table.
+ *
+ * "Unknown" never merges with anything known. The only time a register
+ * transitions from "unknown" to "known" is when we're executing code
+ * for the first time, and we handle that with a simple copy.
+ */
+ static const char merge_table_[kRegTypeMAX][kRegTypeMAX];
+
+ /*
+ * Returns "true" if the flags indicate that this address holds the start
+ * of an instruction.
+ */
+ static inline bool InsnIsOpcode(const InsnFlags insn_flags[], int addr) {
+ return (insn_flags[addr] & kInsnFlagWidthMask) != 0;
+ }
+
+ /* Extract the unsigned 16-bit instruction width from "flags". */
+ static inline int InsnGetWidth(const InsnFlags insn_flags[], int addr) {
+ return insn_flags[addr] & kInsnFlagWidthMask;
+ }
+
+ /* Utilities to check and set kInsnFlagChanged. */
+ static inline bool InsnIsChanged(const InsnFlags insn_flags[], int addr) {
+ return (insn_flags[addr] & kInsnFlagChanged) != 0;
+ }
+ static inline void InsnSetChanged(InsnFlags insn_flags[], int addr,
+ bool changed) {
+ if (changed)
+ insn_flags[addr] |= kInsnFlagChanged;
+ else
+ insn_flags[addr] &= ~kInsnFlagChanged;
+ }
+
+ /* Utilities to check and set kInsnFlagVisited. */
+ static inline bool InsnIsVisited(const InsnFlags insn_flags[], int addr) {
+ return (insn_flags[addr] & kInsnFlagVisited) != 0;
+ }
+ static inline void InsnSetVisited(InsnFlags insn_flags[], int addr,
+ bool visited) {
+ if (visited)
+ insn_flags[addr] |= kInsnFlagVisited;
+ else
+ insn_flags[addr] &= ~kInsnFlagVisited;
+ }
+
+ static inline bool InsnIsVisitedOrChanged(const InsnFlags insn_flags[],
+ int addr) {
+ return (insn_flags[addr] & (kInsnFlagVisited |
+ kInsnFlagChanged)) != 0;
+ }
+
+ /* Utilities to check and set kInsnFlagInTry. */
+ static inline bool InsnIsInTry(const InsnFlags insn_flags[], int addr) {
+ return (insn_flags[addr] & kInsnFlagInTry) != 0;
+ }
+ static inline void InsnSetInTry(InsnFlags insn_flags[], int addr) {
+ insn_flags[addr] |= kInsnFlagInTry;
+ }
+
+ /* Utilities to check and set kInsnFlagBranchTarget. */
+ static inline bool InsnIsBranchTarget(const InsnFlags insn_flags[], int addr)
+ {
+ return (insn_flags[addr] & kInsnFlagBranchTarget) != 0;
+ }
+ static inline void InsnSetBranchTarget(InsnFlags insn_flags[], int addr) {
+ insn_flags[addr] |= kInsnFlagBranchTarget;
+ }
+
+ /* Utilities to check and set kInsnFlagGcPoint. */
+ static inline bool InsnIsGcPoint(const InsnFlags insn_flags[], int addr) {
+ return (insn_flags[addr] & kInsnFlagGcPoint) != 0;
+ }
+ static inline void InsnSetGcPoint(InsnFlags insn_flags[], int addr) {
+ insn_flags[addr] |= kInsnFlagGcPoint;
+ }
+
+ /* Get the class object at the specified index. */
+ static inline Class* GetUninitInstance(const UninitInstanceMap* uninit_map,
+ int idx) {
+ assert(idx >= 0 && idx < uninit_map->num_entries_);
+ return uninit_map->map_[idx].klass_;
+ }
+
+ /* Determine if "type" is actually an object reference (init/uninit/zero) */
+ static inline bool RegTypeIsReference(RegType type) {
+ return (type > kRegTypeMAX || type == kRegTypeUninit ||
+ type == kRegTypeZero);
+ }
+
+ /* Determine if "type" is an uninitialized object reference */
+ static inline bool RegTypeIsUninitReference(RegType type) {
+ return ((type & kRegTypeUninitMask) == kRegTypeUninit);
+ }
+
+ /*
+ * Convert the initialized reference "type" to a Class pointer
+ * (does not expect uninit ref types or "zero").
+ */
+ static Class* RegTypeInitializedReferenceToClass(RegType type) {
+ assert(RegTypeIsReference(type) && type != kRegTypeZero);
+ if ((type & 0x01) == 0) {
+ return (Class*) type;
+ } else {
+ LOG(ERROR) << "VFY: attempted to use uninitialized reference";
+ return NULL;
+ }
+ }
+
+ /* Extract the index into the uninitialized instance map table. */
+ static inline int RegTypeToUninitIndex(RegType type) {
+ assert(RegTypeIsUninitReference(type));
+ return (type & ~kRegTypeUninitMask) >> kRegTypeUninitShift;
+ }
+
+ /* Convert the reference "type" to a Class pointer. */
+ static Class* RegTypeReferenceToClass(RegType type,
+ const UninitInstanceMap* uninit_map) {
+ assert(RegTypeIsReference(type) && type != kRegTypeZero);
+ if (RegTypeIsUninitReference(type)) {
+ assert(uninit_map != NULL);
+ return GetUninitInstance(uninit_map, RegTypeToUninitIndex(type));
+ } else {
+ return (Class*) type;
+ }
+ }
+
+ /* Convert the ClassObject pointer to an (initialized) register type. */
+ static inline RegType RegTypeFromClass(Class* klass) {
+ return (uint32_t) klass;
+ }
+
+ /* Return the RegType for the uninitialized reference in slot "uidx". */
+ static inline RegType RegTypeFromUninitIndex(int uidx) {
+ return (uint32_t) (kRegTypeUninit | (uidx << kRegTypeUninitShift));
+ }
+
+ /* Verify a class. Returns "true" on success. */
static bool VerifyClass(Class* klass);
private:
+ /*
+ * Perform verification on a single method.
+ *
+ * We do this in three passes:
+ * (1) Walk through all code units, determining instruction locations,
+ * widths, and other characteristics.
+ * (2) Walk through all code units, performing static checks on
+ * operands.
+ * (3) Iterate through the method, checking type safety and looking
+ * for code flow problems.
+ *
+ * Some checks may be bypassed depending on the verification mode. We can't
+ * turn this stuff off completely if we want to do "exact" GC.
+ *
+ * Confirmed here:
+ * - code array must not be empty
+ * Confirmed by ComputeWidthsAndCountOps():
+ * - opcode of first instruction begins at index 0
+ * - only documented instructions may appear
+ * - each instruction follows the last
+ * - last byte of last instruction is at (code_length-1)
+ */
static bool VerifyMethod(Method* method);
- static bool VerifyInstructions(const DexFile* dex_file,
- const DexFile::CodeItem* code_item,
- uint32_t insn_flags[]);
- static bool VerifyInstruction(const DexFile* dex_file,
- const Instruction* inst,
- uint32_t code_offset,
- const DexFile::CodeItem* code_item,
- uint32_t insn_flags[]);
- DISALLOW_COPY_AND_ASSIGN(DexVerify);
+ /*
+ * Perform static verification on all instructions in a method.
+ *
+ * Walks through instructions in a method calling VerifyInstruction on each.
+ */
+ static bool VerifyInstructions(VerifierData* vdata);
+
+ /*
+ * Perform static verification on an instruction.
+ *
+ * As a side effect, this sets the "branch target" flags in InsnFlags.
+ *
+ * "(CF)" items are handled during code-flow analysis.
+ *
+ * v3 4.10.1
+ * - target of each jump and branch instruction must be valid
+ * - targets of switch statements must be valid
+ * - operands referencing constant pool entries must be valid
+ * - (CF) operands of getfield, putfield, getstatic, putstatic must be valid
+ * - (CF) operands of method invocation instructions must be valid
+ * - (CF) only invoke-direct can call a method starting with '<'
+ * - (CF) <clinit> must never be called explicitly
+ * - operands of instanceof, checkcast, new (and variants) must be valid
+ * - new-array[-type] limited to 255 dimensions
+ * - can't use "new" on an array class
+ * - (?) limit dimensions in multi-array creation
+ * - local variable load/store register values must be in valid range
+ *
+ * v3 4.11.1.2
+ * - branches must be within the bounds of the code array
+ * - targets of all control-flow instructions are the start of an instruction
+ * - register accesses fall within range of allocated registers
+ * - (N/A) access to constant pool must be of appropriate type
+ * - code does not end in the middle of an instruction
+ * - execution cannot fall off the end of the code
+ * - (earlier) for each exception handler, the "try" area must begin and
+ * end at the start of an instruction (end can be at the end of the code)
+ * - (earlier) for each exception handler, the handler must start at a valid
+ * instruction
+ */
+ static bool VerifyInstruction(VerifierData* vdata,
+ const Instruction* inst, uint32_t code_offset);
+
+ /* Perform detailed code-flow analysis on a single method. */
+ static bool VerifyCodeFlow(VerifierData* vdata);
+
+ /*
+ * Compute the width of the instruction at each address in the instruction
+ * stream, and store it in vdata->insn_flags. Addresses that are in the
+ * middle of an instruction, or that are part of switch table data, are not
+ * touched (so the caller should probably initialize "insn_flags" to zero).
+ *
+ * The "new_instance_count_" and "monitor_enter_count_" fields in vdata are
+ * also set.
+ *
+ * Performs some static checks, notably:
+ * - opcode of first instruction begins at index 0
+ * - only documented instructions may appear
+ * - each instruction follows the last
+ * - last byte of last instruction is at (code_length-1)
+ *
+ * Logs an error and returns "false" on failure.
+ */
+ static bool ComputeWidthsAndCountOps(VerifierData* vdata);
+
+ /*
+ * Set the "in try" flags for all instructions protected by "try" statements.
+ * Also sets the "branch target" flags for exception handlers.
+ *
+ * Call this after widths have been set in "insn_flags".
+ *
+ * Returns "false" if something in the exception table looks fishy, but
+ * we're expecting the exception table to be somewhat sane.
+ */
+ static bool ScanTryCatchBlocks(VerifierData* vdata);
+
+ /*
+ * Extract the relative offset from a branch instruction.
+ *
+ * Returns "false" on failure (e.g. this isn't a branch instruction).
+ */
+ static bool GetBranchOffset(const DexFile::CodeItem* code_item,
+ const InsnFlags insn_flags[], uint32_t cur_offset, int32_t* pOffset,
+ bool* pConditional, bool* selfOkay);
+
+ /*
+ * Verify an array data table. "cur_offset" is the offset of the
+ * fill-array-data instruction.
+ */
+ static bool CheckArrayData(const DexFile::CodeItem* code_item,
+ uint32_t cur_offset);
+
+ /*
+ * Perform static checks on a "new-instance" instruction. Specifically,
+ * make sure the class reference isn't for an array class.
+ *
+ * We don't need the actual class, just a pointer to the class name.
+ */
+ static bool CheckNewInstance(const DexFile* dex_file, uint32_t idx);
+
+ /*
+ * Perform static checks on a "new-array" instruction. Specifically, make
+ * sure they aren't creating an array of arrays that causes the number of
+ * dimensions to exceed 255.
+ */
+ static bool CheckNewArray(const DexFile* dex_file, uint32_t idx);
+
+ /*
+ * Perform static checks on an instruction that takes a class constant.
+ * Ensure that the class index is in the valid range.
+ */
+ static bool CheckTypeIndex(const DexFile* dex_file, uint32_t idx);
+
+ /*
+ * Perform static checks on a field get or set instruction. All we do
+ * here is ensure that the field index is in the valid range.
+ */
+ static bool CheckFieldIndex(const DexFile* dex_file, uint32_t idx);
+
+ /*
+ * Perform static checks on a method invocation instruction. All we do
+ * here is ensure that the method index is in the valid range.
+ */
+ static bool CheckMethodIndex(const DexFile* dex_file, uint32_t idx);
+
+ /* Ensure that the string index is in the valid range. */
+ static bool CheckStringIndex(const DexFile* dex_file, uint32_t idx);
+
+ /* Ensure that the register index is valid for this code item. */
+ static bool CheckRegisterIndex(const DexFile::CodeItem* code_item,
+ uint32_t idx);
+
+ /* Ensure that the wide register index is valid for this code item. */
+ static bool CheckWideRegisterIndex(const DexFile::CodeItem* code_item,
+ uint32_t idx);
+
+ /*
+ * Check the register indices used in a "vararg" instruction, such as
+ * invoke-virtual or filled-new-array.
+ *
+ * vA holds word count (0-5), args[] have values.
+ *
+ * There are some tests we don't do here, e.g. we don't try to verify
+ * that invoking a method that takes a double is done with consecutive
+ * registers. This requires parsing the target method signature, which
+ * we will be doing later on during the code flow analysis.
+ */
+ static bool CheckVarArgRegs(const DexFile::CodeItem* code_item, uint32_t vA,
+ uint32_t arg[]);
+
+ /*
+ * Check the register indices used in a "vararg/range" instruction, such as
+ * invoke-virtual/range or filled-new-array/range.
+ *
+ * vA holds word count, vC holds index of first reg.
+ */
+ static bool CheckVarArgRangeRegs(const DexFile::CodeItem* code_item,
+ uint32_t vA, uint32_t vC);
+
+ /*
+ * Verify a switch table. "cur_offset" is the offset of the switch
+ * instruction.
+ *
+ * Updates "insnFlags", setting the "branch target" flag.
+ */
+ static bool CheckSwitchTargets(const DexFile::CodeItem* code_item,
+ InsnFlags insn_flags[], uint32_t cur_offset);
+
+ /*
+ * Verify that the target of a branch instruction is valid.
+ *
+ * We don't expect code to jump directly into an exception handler, but
+ * it's valid to do so as long as the target isn't a "move-exception"
+ * instruction. We verify that in a later stage.
+ *
+ * The dex format forbids certain instructions from branching to itself.
+ *
+ * Updates "insnFlags", setting the "branch target" flag.
+ */
+ static bool CheckBranchTarget(const DexFile::CodeItem* code_item,
+ InsnFlags insn_flags[], uint32_t cur_offset);
+
+ /*
+ * Initialize the RegisterTable.
+ *
+ * Every instruction address can have a different set of information about
+ * what's in which register, but for verification purposes we only need to
+ * store it at branch target addresses (because we merge into that).
+ *
+ * By zeroing out the regType storage we are effectively initializing the
+ * register information to kRegTypeUnknown.
+ *
+ * We jump through some hoops here to minimize the total number of
+ * allocations we have to perform per method verified.
+ */
+ static bool InitRegisterTable(VerifierData* vdata, RegisterTable* reg_table,
+ RegisterTrackingMode track_regs_for);
+
+ /* Get the register line for the given instruction in the current method. */
+ static inline RegisterLine* GetRegisterLine(const RegisterTable* reg_table,
+ int insn_idx) {
+ return ®_table->register_lines_[insn_idx];
+ }
+
+ /* Copy a register line. */
+ static inline void CopyRegisterLine(RegisterLine* dst,
+ const RegisterLine* src, size_t num_regs) {
+ memcpy(dst->reg_types_, src->reg_types_, num_regs * sizeof(RegType));
+
+ assert((src->monitor_entries_ == NULL && dst->monitor_entries_ == NULL) ||
+ (src->monitor_entries_ != NULL && dst->monitor_entries_ != NULL));
+ if (dst->monitor_entries_ != NULL) {
+ assert(dst->monitor_stack_ != NULL);
+ memcpy(dst->monitor_entries_, src->monitor_entries_,
+ num_regs * sizeof(MonitorEntries));
+ memcpy(dst->monitor_stack_, src->monitor_stack_,
+ kMaxMonitorStackDepth * sizeof(uint32_t));
+ dst->monitor_stack_top_ = src->monitor_stack_top_;
+ }
+ }
+
+ /* Copy a register line into the table. */
+ static inline void CopyLineToTable(RegisterTable* reg_table, int insn_idx,
+ const RegisterLine* src) {
+ RegisterLine* dst = GetRegisterLine(reg_table, insn_idx);
+ assert(dst->reg_types_ != NULL);
+ CopyRegisterLine(dst, src, reg_table->insn_reg_count_plus_);
+ }
+
+ /* Copy a register line out of the table. */
+ static inline void CopyLineFromTable(RegisterLine* dst,
+ const RegisterTable* reg_table, int insn_idx) {
+ RegisterLine* src = GetRegisterLine(reg_table, insn_idx);
+ assert(src->reg_types_ != NULL);
+ CopyRegisterLine(dst, src, reg_table->insn_reg_count_plus_);
+ }
+
+#ifndef NDEBUG
+ /*
+ * Compare two register lines. Returns 0 if they match.
+ *
+ * Using this for a sort is unwise, since the value can change based on
+ * machine endianness.
+ */
+ static inline int CompareLineToTable(const RegisterTable* reg_table,
+ int insn_idx, const RegisterLine* line2) {
+ const RegisterLine* line1 = GetRegisterLine(reg_table, insn_idx);
+ if (line1->monitor_entries_ != NULL) {
+ int result;
+
+ if (line2->monitor_entries_ == NULL)
+ return 1;
+ result = memcmp(line1->monitor_entries_, line2->monitor_entries_,
+ reg_table->insn_reg_count_plus_ * sizeof(MonitorEntries));
+ if (result != 0) {
+ LOG(ERROR) << "monitor_entries_ mismatch";
+ return result;
+ }
+ result = line1->monitor_stack_top_ - line2->monitor_stack_top_;
+ if (result != 0) {
+ LOG(ERROR) << "monitor_stack_top_ mismatch";
+ return result;
+ }
+ result = memcmp(line1->monitor_stack_, line2->monitor_stack_,
+ line1->monitor_stack_top_);
+ if (result != 0) {
+ LOG(ERROR) << "monitor_stack_ mismatch";
+ return result;
+ }
+ }
+ return memcmp(line1->reg_types_, line2->reg_types_,
+ reg_table->insn_reg_count_plus_ * sizeof(RegType));
+ }
+#endif
+
+ /*
+ * Create a new uninitialized instance map.
+ *
+ * The map is allocated and populated with address entries. The addresses
+ * appear in ascending order to allow binary searching.
+ *
+ * Very few methods have 10 or more new-instance instructions; the
+ * majority have 0 or 1. Occasionally a static initializer will have 200+.
+ *
+ * TODO: merge this into the static pass or initRegisterTable; want to
+ * avoid walking through the instructions yet again just to set up this table
+ */
+ static UninitInstanceMap* CreateUninitInstanceMap(VerifierData* vdata);
+
+ /* Returns true if this method is a constructor. */
+ static bool IsInitMethod(const Method* method);
+
+ /*
+ * Look up a class reference given as a simple string descriptor.
+ *
+ * If we can't find it, return a generic substitute when possible.
+ */
+ static Class* LookupClassByDescriptor(const Method* method,
+ const char* descriptor, VerifyError* failure);
+
+ /*
+ * Look up a class reference in a signature. Could be an arg or the
+ * return value.
+ *
+ * Advances "*sig" to the last character in the signature (that is, to
+ * the ';').
+ *
+ * NOTE: this is also expected to verify the signature.
+ */
+ static Class* LookupSignatureClass(const Method* method, std::string sig,
+ VerifyError* failure);
+
+ /*
+ * Look up an array class reference in a signature. Could be an arg or the
+ * return value.
+ *
+ * Advances "*sig" to the last character in the signature.
+ *
+ * NOTE: this is also expected to verify the signature.
+ */
+ static Class* LookupSignatureArrayClass(const Method* method,
+ std::string sig, VerifyError* failure);
+
+ /*
+ * Set the register types for the first instruction in the method based on
+ * the method signature.
+ *
+ * This has the side-effect of validating the signature.
+ *
+ * Returns "true" on success.
+ */
+ static bool SetTypesFromSignature(VerifierData* vdata, RegType* reg_types);
+
+ /*
+ * Set the class object associated with the instruction at "addr".
+ *
+ * Returns the map slot index, or -1 if the address isn't listed in the map
+ * (shouldn't happen) or if a class is already associated with the address
+ * (bad bytecode).
+ *
+ * Entries, once set, do not change -- a given address can only allocate
+ * one type of object.
+ */
+ static int SetUninitInstance(UninitInstanceMap* uninit_map, int addr,
+ Class* klass);
+
+ /*
+ * Perform code flow on a method.
+ *
+ * The basic strategy is as outlined in v3 4.11.1.2: set the "changed" bit
+ * on the first instruction, process it (setting additional "changed" bits),
+ * and repeat until there are no more.
+ *
+ * v3 4.11.1.1
+ * - (N/A) operand stack is always the same size
+ * - operand stack [registers] contain the correct types of values
+ * - local variables [registers] contain the correct types of values
+ * - methods are invoked with the appropriate arguments
+ * - fields are assigned using values of appropriate types
+ * - opcodes have the correct type values in operand registers
+ * - there is never an uninitialized class instance in a local variable in
+ * code protected by an exception handler (operand stack is okay, because
+ * the operand stack is discarded when an exception is thrown) [can't
+ * know what's a local var w/o the debug info -- should fall out of
+ * register typing]
+ *
+ * v3 4.11.1.2
+ * - execution cannot fall off the end of the code
+ *
+ * (We also do many of the items described in the "static checks" sections,
+ * because it's easier to do them here.)
+ *
+ * We need an array of RegType values, one per register, for every
+ * instruction. If the method uses monitor-enter, we need extra data
+ * for every register, and a stack for every "interesting" instruction.
+ * In theory this could become quite large -- up to several megabytes for
+ * a monster function.
+ *
+ * NOTE:
+ * The spec forbids backward branches when there's an uninitialized reference
+ * in a register. The idea is to prevent something like this:
+ * loop:
+ * move r1, r0
+ * new-instance r0, MyClass
+ * ...
+ * if-eq rN, loop // once
+ * initialize r0
+ *
+ * This leaves us with two different instances, both allocated by the
+ * same instruction, but only one is initialized. The scheme outlined in
+ * v3 4.11.1.4 wouldn't catch this, so they work around it by preventing
+ * backward branches. We achieve identical results without restricting
+ * code reordering by specifying that you can't execute the new-instance
+ * instruction if a register contains an uninitialized instance created
+ * by that same instrutcion.
+ */
+ static bool CodeFlowVerifyMethod(VerifierData* vdata,
+ RegisterTable* reg_table);
+
+ /*
+ * Perform verification for a single instruction.
+ *
+ * This requires fully decoding the instruction to determine the effect
+ * it has on registers.
+ *
+ * Finds zero or more following instructions and sets the "changed" flag
+ * if execution at that point needs to be (re-)evaluated. Register changes
+ * are merged into "reg_types_" at the target addresses. Does not set or
+ * clear any other flags in "insn_flags".
+ */
+ static bool CodeFlowVerifyInstruction(VerifierData* vdata,
+ RegisterTable* reg_table, uint32_t insn_idx, size_t* start_guess);
+
+ /*
+ * Replace an instruction with "throw-verification-error". This allows us to
+ * defer error reporting until the code path is first used.
+ *
+ * This is expected to be called during "just in time" verification, not
+ * from within dexopt. (Verification failures in dexopt will result in
+ * postponement of verification to first use of the class.)
+ *
+ * The throw-verification-error instruction requires two code units. Some
+ * of the replaced instructions require three; the third code unit will
+ * receive a "nop". The instruction's length will be left unchanged
+ * in "insn_flags".
+ *
+ * The VM postpones setting of debugger breakpoints in unverified classes,
+ * so there should be no clashes with the debugger.
+ *
+ * Returns "true" on success.
+ */
+ static bool ReplaceFailingInstruction(const DexFile::CodeItem* code_item,
+ InsnFlags* insn_flags, int insn_idx, VerifyError failure);
+
+ /* Handle a monitor-enter instruction. */
+ static void HandleMonitorEnter(RegisterLine* work_line, uint32_t reg_idx,
+ uint32_t insn_idx, VerifyError* failure);
+
+ /* Handle a monitor-exit instruction. */
+ static void HandleMonitorExit(RegisterLine* work_line, uint32_t reg_idx,
+ uint32_t insn_idx, VerifyError* failure);
+
+ /*
+ * Look up an instance field, specified by "field_idx", that is going to be
+ * accessed in object "obj_type". This resolves the field and then verifies
+ * that the class containing the field is an instance of the reference in
+ * "obj_type".
+ *
+ * It is possible for "obj_type" to be kRegTypeZero, meaning that we might
+ * have a null reference. This is a runtime problem, so we allow it,
+ * skipping some of the type checks.
+ *
+ * In general, "obj_type" must be an initialized reference. However, we
+ * allow it to be uninitialized if this is an "<init>" method and the field
+ * is declared within the "obj_type" class.
+ *
+ * Returns a Field on success, returns NULL and sets "*failure" on failure.
+ */
+ static Field* GetInstField(VerifierData* vdata, RegType obj_type,
+ int field_idx, VerifyError* failure);
+
+ /*
+ * Look up a static field.
+ *
+ * Returns a StaticField on success, returns NULL and sets "*failure"
+ * on failure.
+ */
+ static Field* GetStaticField(VerifierData* vdata, int field_idx,
+ VerifyError* failure);
+ /*
+ * For the "move-exception" instruction at "insn_idx", which must be at an
+ * exception handler address, determine the first common superclass of
+ * all exceptions that can land here. (For javac output, we're probably
+ * looking at multiple spans of bytecode covered by one "try" that lands
+ * at an exception-specific "catch", but in general the handler could be
+ * shared for multiple exceptions.)
+ *
+ * Returns NULL if no matching exception handler can be found, or if the
+ * exception is not a subclass of Throwable.
+ */
+ static Class* GetCaughtExceptionType(VerifierData* vdata, int insn_idx,
+ VerifyError* failure);
+
+ /*
+ * Get the type of register N.
+ *
+ * The register index was validated during the static pass, so we don't
+ * need to check it here.
+ */
+ static inline RegType GetRegisterType(const RegisterLine* register_line,
+ uint32_t vsrc) {
+ return register_line->reg_types_[vsrc];
+ }
+
+ /*
+ * Return the register type for the method. We can't just use the
+ * already-computed DalvikJniReturnType, because if it's a reference type
+ * we need to do the class lookup.
+ *
+ * Returned references are assumed to be initialized.
+ *
+ * Returns kRegTypeUnknown for "void".
+ */
+ static RegType GetMethodReturnType(const DexFile* dex_file,
+ const Method* method);
+
+ /*
+ * Get the value from a register, and cast it to a Class. Sets
+ * "*failure" if something fails.
+ *
+ * This fails if the register holds an uninitialized class.
+ *
+ * If the register holds kRegTypeZero, this returns a NULL pointer.
+ */
+ static Class* GetClassFromRegister(const RegisterLine* register_line,
+ uint32_t vsrc, VerifyError* failure);
+
+ /*
+ * Get the "this" pointer from a non-static method invocation. This
+ * returns the RegType so the caller can decide whether it needs the
+ * reference to be initialized or not. (Can also return kRegTypeZero
+ * if the reference can only be zero at this point.)
+ *
+ * The argument count is in vA, and the first argument is in vC, for both
+ * "simple" and "range" versions. We just need to make sure vA is >= 1
+ * and then return vC.
+ */
+ static RegType GetInvocationThis(const RegisterLine* register_line,
+ const Instruction::DecodedInstruction* dec_insn, VerifyError* failure);
+
+ /*
+ * Set the type of register N, verifying that the register is valid. If
+ * "new_type" is the "Lo" part of a 64-bit value, register N+1 will be
+ * set to "new_type+1".
+ *
+ * The register index was validated during the static pass, so we don't
+ * need to check it here.
+ *
+ * TODO: clear mon stack bits
+ */
+ static void SetRegisterType(RegisterLine* register_line, uint32_t vdst,
+ RegType new_type);
+
+ /*
+ * Verify that the contents of the specified register have the specified
+ * type (or can be converted to it through an implicit widening conversion).
+ *
+ * This will modify the type of the source register if it was originally
+ * derived from a constant to prevent mixing of int/float and long/double.
+ *
+ * If "vsrc" is a reference, both it and the "vsrc" register must be
+ * initialized ("vsrc" may be Zero). This will verify that the value in
+ * the register is an instance of check_type, or if check_type is an
+ * interface, verify that the register implements check_type.
+ */
+ static void VerifyRegisterType(RegisterLine* register_line, uint32_t vsrc,
+ RegType check_type, VerifyError* failure);
+
+ /* Set the type of the "result" register. */
+ static void SetResultRegisterType(RegisterLine* register_line,
+ const int insn_reg_count, RegType new_type);
+
+ /*
+ * Update all registers holding "uninit_type" to instead hold the
+ * corresponding initialized reference type. This is called when an
+ * appropriate <init> method is invoked -- all copies of the reference
+ * must be marked as initialized.
+ */
+ static void MarkRefsAsInitialized(RegisterLine* register_line,
+ int insn_reg_count, UninitInstanceMap* uninit_map, RegType uninit_type,
+ VerifyError* failure);
+
+ /*
+ * Implement category-1 "move" instructions. Copy a 32-bit value from
+ * "vsrc" to "vdst".
+ */
+ static void CopyRegister1(RegisterLine* register_line, uint32_t vdst,
+ uint32_t vsrc, TypeCategory cat, VerifyError* failure);
+
+ /*
+ * Implement category-2 "move" instructions. Copy a 64-bit value from
+ * "vsrc" to "vdst". This copies both halves of the register.
+ */
+ static void CopyRegister2(RegisterLine* register_line, uint32_t vdst,
+ uint32_t vsrc, VerifyError* failure);
+
+ /*
+ * Implement "move-result". Copy the category-1 value from the result
+ * register to another register, and reset the result register.
+ */
+ static void CopyResultRegister1(RegisterLine* register_line,
+ const int insn_reg_count, uint32_t vdst, TypeCategory cat,
+ VerifyError* failure);
+
+ /*
+ * Implement "move-result-wide". Copy the category-2 value from the result
+ * register to another register, and reset the result register.
+ */
+ static void CopyResultRegister2(RegisterLine* register_line,
+ const int insn_reg_count, uint32_t vdst, VerifyError* failure);
+
+ /*
+ * Compute the "class depth" of a class. This is the distance from the
+ * class to the top of the tree, chasing superclass links. java.lang.Object
+ * has a class depth of 0.
+ */
+ static int GetClassDepth(Class* klass);
+
+ /*
+ * Given two classes, walk up the superclass tree to find a common
+ * ancestor. (Called from findCommonSuperclass().)
+ *
+ * TODO: consider caching the class depth in the class object so we don't
+ * have to search for it here.
+ */
+ static Class* DigForSuperclass(Class* c1, Class* c2);
+
+ /*
+ * Merge two array classes. We can't use the general "walk up to the
+ * superclass" merge because the superclass of an array is always Object.
+ * We want String[] + Integer[] = Object[]. This works for higher dimensions
+ * as well, e.g. String[][] + Integer[][] = Object[][].
+ *
+ * If Foo1 and Foo2 are subclasses of Foo, Foo1[] + Foo2[] = Foo[].
+ *
+ * If Class implements Type, Class[] + Type[] = Type[].
+ *
+ * If the dimensions don't match, we want to convert to an array of Object
+ * with the least dimension, e.g. String[][] + String[][][][] = Object[][].
+ *
+ * Arrays of primitive types effectively have one less dimension when
+ * merging. int[] + float[] = Object, int[] + String[] = Object,
+ * int[][] + float[][] = Object[], int[][] + String[] = Object[]. (The
+ * only time this function doesn't return an array class is when one of
+ * the arguments is a 1-dimensional primitive array.)
+ *
+ * This gets a little awkward because we may have to ask the VM to create
+ * a new array type with the appropriate element and dimensions. However, we
+ * shouldn't be doing this often.
+ */
+ static Class* FindCommonArraySuperclass(Class* c1, Class* c2);
+
+ /*
+ * Find the first common superclass of the two classes. We're not
+ * interested in common interfaces.
+ *
+ * The easiest way to do this for concrete classes is to compute the "class
+ * depth" of each, move up toward the root of the deepest one until they're
+ * at the same depth, then walk both up to the root until they match.
+ *
+ * If both classes are arrays, we need to merge based on array depth and
+ * element type.
+ *
+ * If one class is an interface, we check to see if the other class/interface
+ * (or one of its predecessors) implements the interface. If so, we return
+ * the interface; otherwise, we return Object.
+ *
+ * NOTE: we continue the tradition of "lazy interface handling". To wit,
+ * suppose we have three classes:
+ * One implements Fancy, Free
+ * Two implements Fancy, Free
+ * Three implements Free
+ * where Fancy and Free are unrelated interfaces. The code requires us
+ * to merge One into Two. Ideally we'd use a common interface, which
+ * gives us a choice between Fancy and Free, and no guidance on which to
+ * use. If we use Free, we'll be okay when Three gets merged in, but if
+ * we choose Fancy, we're hosed. The "ideal" solution is to create a
+ * set of common interfaces and carry that around, merging further references
+ * into it. This is a pain. The easy solution is to simply boil them
+ * down to Objects and let the runtime invokeinterface call fail, which
+ * is what we do.
+ */
+ static Class* FindCommonSuperclass(Class* c1, Class* c2);
+
+ /*
+ * Merge two RegType values.
+ *
+ * Sets "*changed" to "true" if the result doesn't match "type1".
+ */
+ static RegType MergeTypes(RegType type1, RegType type2, bool* changed);
+
+ /*
+ * Merge the bits that indicate which monitor entry addresses on the stack
+ * are associated with this register.
+ *
+ * The merge is a simple bitwise AND.
+ *
+ * Sets "*pChanged" to "true" if the result doesn't match "ents1".
+ */
+ static MonitorEntries MergeMonitorEntries(MonitorEntries ents1,
+ MonitorEntries ents2, bool* changed);
+
+ /*
+ * We're creating a new instance of class C at address A. Any registers
+ * holding instances previously created at address A must be initialized
+ * by now. If not, we mark them as "conflict" to prevent them from being
+ * used (otherwise, MarkRefsAsInitialized would mark the old ones and the
+ * new ones at the same time).
+ */
+ static void MarkUninitRefsAsInvalid(RegisterLine* register_line,
+ int insn_reg_count, UninitInstanceMap* uninit_map, RegType uninit_type);
+
+ /*
+ * Control can transfer to "next_insn".
+ *
+ * Merge the registers from "work_line" into "reg_table" at "next_insn", and
+ * set the "changed" flag on the target address if any of the registers
+ * has changed.
+ *
+ * Returns "false" if we detect mismatched monitor stacks.
+ */
+ static bool UpdateRegisters(InsnFlags* insn_flags, RegisterTable* reg_table,
+ int next_insn, const RegisterLine* work_line);
+
+ /*
+ * Determine whether we can convert "src_type" to "check_type", where
+ * "check_type" is one of the category-1 non-reference types.
+ *
+ * Constant derived types may become floats, but other values may not.
+ */
+ static bool CanConvertTo1nr(RegType src_type, RegType check_type);
+
+ /* Determine whether the category-2 types are compatible. */
+ static bool CanConvertTo2(RegType src_type, RegType check_type);
+
+ /* Convert a VM PrimitiveType enum value to the equivalent RegType value. */
+ static RegType PrimitiveTypeToRegType(Class::PrimitiveType prim_type);
+
+ /*
+ * Convert a const derived RegType to the equivalent non-const RegType value.
+ * Does nothing if the argument type isn't const derived.
+ */
+ static RegType ConstTypeToRegType(RegType const_type);
+
+ /*
+ * Given a 32-bit constant, return the most-restricted RegType enum entry
+ * that can hold the value. The types used here indicate the value came
+ * from a const instruction, and may not correctly represent the real type
+ * of the value. Upon use, a constant derived type is updated with the
+ * type from the use, which will be unambiguous.
+ */
+ static char DetermineCat1Const(int32_t value);
+
+ /*
+ * If "field" is marked "final", make sure this is the either <clinit>
+ * or <init> as appropriate.
+ *
+ * Sets "*failure" on failure.
+ */
+ static void CheckFinalFieldAccess(const Method* method, const Field* field,
+ VerifyError* failure);
+
+ /*
+ * Make sure that the register type is suitable for use as an array index.
+ *
+ * Sets "*failure" if not.
+ */
+ static void CheckArrayIndexType(const Method* method, RegType reg_type,
+ VerifyError* failure);
+
+ /*
+ * Check constraints on constructor return. Specifically, make sure that
+ * the "this" argument got initialized.
+ *
+ * The "this" argument to <init> uses code offset kUninitThisArgAddr, which
+ * puts it at the start of the list in slot 0. If we see a register with
+ * an uninitialized slot 0 reference, we know it somehow didn't get
+ * initialized.
+ *
+ * Returns "true" if all is well.
+ */
+ static bool CheckConstructorReturn(const Method* method,
+ const RegisterLine* register_line, const int insn_reg_count);
+
+ /*
+ * Verify that the target instruction is not "move-exception". It's important
+ * that the only way to execute a move-exception is as the first instruction
+ * of an exception handler.
+ *
+ * Returns "true" if all is well, "false" if the target instruction is
+ * move-exception.
+ */
+ static bool CheckMoveException(const uint16_t* insns, int insn_idx);
+
+ /*
+ * See if "type" matches "cat". All we're really looking for here is that
+ * we're not mixing and matching 32-bit and 64-bit quantities, and we're
+ * not mixing references with numerics. (For example, the arguments to
+ * "a < b" could be integers of different sizes, but they must both be
+ * integers. Dalvik is less specific about int vs. float, so we treat them
+ * as equivalent here.)
+ *
+ * For category 2 values, "type" must be the "low" half of the value.
+ *
+ * Sets "*failure" if something looks wrong.
+ */
+ static void CheckTypeCategory(RegType type, TypeCategory cat,
+ VerifyError* failure);
+
+ /*
+ * For a category 2 register pair, verify that "type_h" is the appropriate
+ * high part for "type_l".
+ *
+ * Does not verify that "type_l" is in fact the low part of a 64-bit
+ * register pair.
+ */
+ static void CheckWidePair(RegType type_l, RegType type_h,
+ VerifyError* failure);
+
+ /*
+ * Verify types for a simple two-register instruction (e.g. "neg-int").
+ * "dst_type" is stored into vA, and "src_type" is verified against vB.
+ */
+ static void CheckUnop(RegisterLine* register_line,
+ Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+ RegType src_type, VerifyError* failure);
+
+ /*
+ * Verify types for a simple three-register instruction (e.g. "add-int").
+ * "dst_type" is stored into vA, and "src_type1"/"src_type2" are verified
+ * against vB/vC.
+ */
+ static void CheckBinop(RegisterLine* register_line,
+ Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+ RegType src_type1, RegType src_type2, bool check_boolean_op,
+ VerifyError* failure);
+
+ /*
+ * Verify types for a binary "2addr" operation. "src_type1"/"src_type2"
+ * are verified against vA/vB, then "dst_type" is stored into vA.
+ */
+ static void CheckBinop2addr(RegisterLine* register_line,
+ Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+ RegType src_type1, RegType src_type2, bool check_boolean_op,
+ VerifyError* failure);
+
+ /*
+ * Treat right-shifting as a narrowing conversion when possible.
+ *
+ * For example, right-shifting an int 24 times results in a value that can
+ * be treated as a byte.
+ *
+ * Things get interesting when contemplating sign extension. Right-
+ * shifting an integer by 16 yields a value that can be represented in a
+ * "short" but not a "char", but an unsigned right shift by 16 yields a
+ * value that belongs in a char rather than a short. (Consider what would
+ * happen if the result of the shift were cast to a char or short and then
+ * cast back to an int. If sign extension, or the lack thereof, causes
+ * a change in the 32-bit representation, then the conversion was lossy.)
+ *
+ * A signed right shift by 17 on an integer results in a short. An unsigned
+ * right shfit by 17 on an integer results in a posshort, which can be
+ * assigned to a short or a char.
+ *
+ * An unsigned right shift on a short can actually expand the result into
+ * a 32-bit integer. For example, 0xfffff123 >>> 8 becomes 0x00fffff1,
+ * which can't be represented in anything smaller than an int.
+ *
+ * javac does not generate code that takes advantage of this, but some
+ * of the code optimizers do. It's generally a peephole optimization
+ * that replaces a particular sequence, e.g. (bipush 24, ishr, i2b) is
+ * replaced by (bipush 24, ishr). Knowing that shifting a short 8 times
+ * to the right yields a byte is really more than we need to handle the
+ * code that's out there, but support is not much more complex than just
+ * handling integer.
+ *
+ * Right-shifting never yields a boolean value.
+ *
+ * Returns the new register type.
+ */
+ static RegType AdjustForRightShift(RegisterLine* register_line, int reg,
+ unsigned int shift_count, bool is_unsigned_shift, VerifyError* failure);
+
+ /*
+ * We're performing an operation like "and-int/2addr" that can be
+ * performed on booleans as well as integers. We get no indication of
+ * boolean-ness, but we can infer it from the types of the arguments.
+ *
+ * Assumes we've already validated reg1/reg2.
+ *
+ * TODO: consider generalizing this. The key principle is that the
+ * result of a bitwise operation can only be as wide as the widest of
+ * the operands. You can safely AND/OR/XOR two chars together and know
+ * you still have a char, so it's reasonable for the compiler or "dx"
+ * to skip the int-to-char instruction. (We need to do this for boolean
+ * because there is no int-to-boolean operation.)
+ *
+ * Returns true if both args are Boolean, Zero, or One.
+ */
+ static bool UpcastBooleanOp(RegisterLine* register_line, uint32_t reg1,
+ uint32_t reg2);
+
+ /*
+ * Verify types for A two-register instruction with a literal constant
+ * (e.g. "add-int/lit8"). "dst_type" is stored into vA, and "src_type" is
+ * verified against vB.
+ *
+ * If "check_boolean_op" is set, we use the constant value in vC.
+ */
+ static void CheckLitop(RegisterLine* register_line,
+ Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+ RegType src_type, bool check_boolean_op, VerifyError* failure);
+
+ /*
+ * Verify that the arguments in a filled-new-array instruction are valid.
+ *
+ * "res_class" is the class refered to by dec_insn->vB_.
+ */
+ static void VerifyFilledNewArrayRegs(const Method* method,
+ RegisterLine* register_line,
+ const Instruction::DecodedInstruction* dec_insn, Class* res_class,
+ bool is_range, VerifyError* failure);
+
+ /* See if the method matches the MethodType. */
+ static bool IsCorrectInvokeKind(MethodType method_type, Method* res_method);
+
+ /*
+ * Verify the arguments to a method. We're executing in "method", making
+ * a call to the method reference in vB.
+ *
+ * If this is a "direct" invoke, we allow calls to <init>. For calls to
+ * <init>, the first argument may be an uninitialized reference. Otherwise,
+ * calls to anything starting with '<' will be rejected, as will any
+ * uninitialized reference arguments.
+ *
+ * For non-static method calls, this will verify that the method call is
+ * appropriate for the "this" argument.
+ *
+ * The method reference is in vBBBB. The "is_range" parameter determines
+ * whether we use 0-4 "args" values or a range of registers defined by
+ * vAA and vCCCC.
+ *
+ * Widening conversions on integers and references are allowed, but
+ * narrowing conversions are not.
+ *
+ * Returns the resolved method on success, NULL on failure (with *failure
+ * set appropriately).
+ */
+ static Method* VerifyInvocationArgs(VerifierData* vdata,
+ RegisterLine* register_line, const int insn_reg_count,
+ const Instruction::DecodedInstruction* dec_insn, MethodType method_type,
+ bool is_range, bool is_super, VerifyError* failure);
+
+ DISALLOW_COPY_AND_ASSIGN(DexVerifier);
};
} // namespace art
diff --git a/src/dex_verifier_test.cc b/src/dex_verifier_test.cc
index e6d8096..39e14c4 100644
--- a/src/dex_verifier_test.cc
+++ b/src/dex_verifier_test.cc
@@ -18,7 +18,7 @@
Class* klass = class_linker_->FindSystemClass(descriptor);
// Verify the class
- ASSERT_TRUE(DexVerify::VerifyClass(klass));
+ ASSERT_TRUE(DexVerifier::VerifyClass(klass));
}
void VerifyDexFile(const DexFile* dex, ClassLoader* class_loader) {
@@ -41,7 +41,7 @@
TEST_F(DexVerifierTest, IntMath) {
const ClassLoader* class_loader = LoadDex("IntMath");
Class* klass = class_linker_->FindClass("LIntMath;", class_loader);
- ASSERT_TRUE(DexVerify::VerifyClass(klass));
+ ASSERT_TRUE(DexVerifier::VerifyClass(klass));
}
} // namespace art
diff --git a/src/object.cc b/src/object.cc
index 4384a24..edc5bdd 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -774,6 +774,24 @@
return NULL;
}
+Method* Class::FindInterfaceMethod(const StringPiece& name,
+ const StringPiece& signature) {
+ // Check the current class before checking the interfaces.
+ Method* method = FindVirtualMethod(name, signature);
+ if (method != NULL) {
+ return method;
+ }
+
+ InterfaceEntry* iftable = GetIFTable();
+ for (size_t i = 0; i < GetIFTableCount(); i++) {
+ method = iftable[i].GetInterface()->FindVirtualMethod(name, signature);
+ if (method != NULL) {
+ return method;
+ }
+ }
+ return NULL;
+}
+
Method* Class::FindDeclaredDirectMethod(const StringPiece& name,
const StringPiece& signature) {
for (size_t i = 0; i < NumDirectMethods(); ++i) {
diff --git a/src/object.h b/src/object.h
index 050f571..9106472 100644
--- a/src/object.h
+++ b/src/object.h
@@ -82,6 +82,7 @@
static const uint32_t kAccConstructor = 0x00010000; // method (Dalvik only)
static const uint32_t kAccDeclaredSynchronized = 0x00020000; // method (Dalvik only)
+static const uint32_t kAccWritable = 0x80000000; // method (Dalvik only)
static const uint32_t kAccClassFlagsMask = (kAccPublic
| kAccFinal
@@ -501,6 +502,10 @@
return (GetAccessFlags() & kAccStatic) != 0;
}
+ bool IsFinal() const {
+ return (access_flags_ & kAccFinal) != 0;
+ }
+
uint32_t GetTypeIdx() const;
void SetTypeIdx(uint32_t type_idx);
@@ -1666,6 +1671,9 @@
// method for this class.
Method* FindVirtualMethodForInterface(Method* method);
+ Method* FindInterfaceMethod(const StringPiece& name,
+ const StringPiece& descriptor);
+
Method* FindVirtualMethodForVirtualOrInterface(Method* method) {
if (method->GetDeclaringClass()->IsInterface()) {
return FindVirtualMethodForInterface(method);
@@ -1931,8 +1939,9 @@
new_source_file, false);
}
- private:
bool Implements(const Class* klass) const;
+
+ private:
bool IsArrayAssignableFromArray(const Class* klass) const;
bool IsAssignableFromArray(const Class* klass) const;
bool IsSubClass(const Class* klass) const;
@@ -1977,7 +1986,7 @@
// is 2. Otherwise 0.
int32_t array_rank_;
- // primitive type index, or PRIM_NOT (-1); set for generated prim classes
+ // primitive type index, or kPrimNot (0); set for generated prim classes
PrimitiveType primitive_type_;
// The superclass, or NULL if this is java.lang.Object or a