blob: 2268772b3cc3046e7b9e6d6373558589045b3657 [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
#include "dex_verifier.h"
#include <iostream>
#include "class_linker.h"
#include "dex_cache.h"
#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"
namespace art {
#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 " << PrettyClass(klass);
return false;
}
}
for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
Method* method = klass->GetVirtualMethod(i);
if (!VerifyMethod(method)) {
LOG(ERROR) << "Verifier rejected class " << PrettyClass(klass);
return false;
}
}
return true;
}
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.
*/
vdata.insn_flags_.reset(new InsnFlags[code_item->insns_size_]());
/*
* 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;
}
bool DexVerifier::VerifyInstructions(VerifierData* vdata) {
const DexFile::CodeItem* code_item = vdata->code_item_;
InsnFlags* insn_flags = vdata->insn_flags_.get();
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;
}
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_.get();
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;
}
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;
if (registers_size * insns_size > 4*1024*1024) {
LOG(ERROR) << "VFY: warning: method is huge (regs=" << registers_size
<< " insns_size=" << insns_size << ")";
}
/* Create and initialize register lists. */
if (!InitRegisterTable(vdata, &reg_table, kTrackRegsGcPoints)) {
return false;
}
vdata->register_lines_ = reg_table.register_lines_.get();
/* Allocate a map to hold the classes of uninitialized instances. */
vdata->uninit_map_.reset(CreateUninitInstanceMap(vdata));
/* Initialize register types of method arguments. */
if (!SetTypesFromSignature(vdata, reg_table.register_lines_[0].reg_types_.get())) {
LOG(ERROR) << "VFY: bad signature in " << PrettyMethod(method);
return false;
}
/* Perform code flow verification. */
if (!CodeFlowVerifyMethod(vdata, &reg_table)) {
return false;
}
/* Generate a register map and add it to the method. */
UniquePtr<RegisterMap> map(GenerateRegisterMapV(vdata));
ByteArray* header = ByteArray::Alloc(sizeof(RegisterMapHeader));
ByteArray* data = ByteArray::Alloc(ComputeRegisterMapSize(map.get()));
memcpy(header->GetData(), map.get()->header_, sizeof(RegisterMapHeader));
memcpy(data->GetData(), map.get()->data_, ComputeRegisterMapSize(map.get()));
method->SetRegisterMapHeader(header);
method->SetRegisterMapData(data);
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_.get();
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_.get();
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;
switch (*insns & 0xff) {
case Instruction::GOTO:
*pOffset = ((int16_t) *insns) >> 8;
*pConditional = false;
*selfOkay = false;
break;
case Instruction::GOTO_32:
*pOffset = insns[1] | (((uint32_t) insns[2]) << 16);
*pConditional = false;
*selfOkay = true;
break;
case Instruction::GOTO_16:
*pOffset = (int16_t) insns[1];
*pConditional = false;
*selfOkay = false;
break;
case Instruction::IF_EQ:
case Instruction::IF_NE:
case Instruction::IF_LT:
case Instruction::IF_GE:
case Instruction::IF_GT:
case Instruction::IF_LE:
case Instruction::IF_EQZ:
case Instruction::IF_NEZ:
case Instruction::IF_LTZ:
case Instruction::IF_GEZ:
case Instruction::IF_GTZ:
case Instruction::IF_LEZ:
*pOffset = (int16_t) insns[1];
*pConditional = true;
*selfOkay = false;
break;
default:
return false;
break;
}
return true;
}
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;
const uint16_t* array_data;
int32_t array_data_offset;
DCHECK_LT(cur_offset, insn_count);
/* make sure the start of the array data table is in range */
array_data_offset = insns[1] | (((int32_t) insns[2]) << 16);
if ((int32_t) cur_offset + array_data_offset < 0 ||
cur_offset + array_data_offset + 2 >= insn_count)
{
LOG(ERROR) << "VFY: invalid array data start: at " << cur_offset
<< ", data offset " << array_data_offset << ", count "
<< insn_count;
return false;
}
/* offset to array data table is a relative branch-style offset */
array_data = insns + array_data_offset;
/* make sure the table is 32-bit aligned */
if ((((uint32_t) array_data) & 0x03) != 0) {
LOG(ERROR) << "VFY: unaligned array data table: at " << cur_offset
<< ", data offset " << array_data_offset;
return false;
}
uint32_t value_width = array_data[1];
uint32_t value_count = *(uint32_t*) (&array_data[2]);
uint32_t table_size = 4 + (value_width * value_count + 1) / 2;
/* make sure the end of the switch is in range */
if (cur_offset + array_data_offset + table_size > insn_count) {
LOG(ERROR) << "VFY: invalid array data end: at " << cur_offset
<< ", data offset " << array_data_offset << ", end "
<< cur_offset + array_data_offset + table_size << ", count "
<< insn_count;
return false;
}
return true;
}
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_ << ")";
return false;
}
const char* descriptor = dex_file->dexStringByTypeIdx(idx);
if (descriptor[0] != 'L') {
LOG(ERROR) << "VFY: can't call new-instance on type '"
<< descriptor << "'";
return false;
}
return true;
}
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_ << ")";
return false;
}
int bracket_count = 0;
const char* descriptor = dex_file->dexStringByTypeIdx(idx);
const char* cp = descriptor;
while (*cp++ == '[')
bracket_count++;
if (bracket_count == 0) {
/* The given class must be an array type. */
LOG(ERROR) << "VFY: can't new-array class '" << descriptor
<< "' (not an array)";
return false;
} else if (bracket_count > 255) {
/* It is illegal to create an array of more than 255 dimensions. */
LOG(ERROR) << "VFY: can't new-array class '" << descriptor
<< "' (exceeds limit)";
return false;
}
return true;
}
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_ << ")";
return false;
}
return true;
}
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_ << ")";
return false;
}
return true;
}
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_ << ")";
return false;
}
return true;
}
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_ << ")";
return false;
}
return true;
}
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_ << ")";
return false;
}
return true;
}
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
<< "+1 >= " << code_item->registers_size_ << ")";
return false;
}
return true;
}
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;
if (vA > 5) {
LOG(ERROR) << "VFY: invalid arg count (" << vA << ") in non-range invoke)";
return false;
}
for (idx = 0; idx < vA; idx++) {
if (arg[idx] > registers_size) {
LOG(ERROR) << "VFY: invalid reg index (" << arg[idx]
<< ") in non-range invoke (> " << registers_size << ")";
return false;
}
}
return true;
}
bool DexVerifier::CheckVarArgRangeRegs(const DexFile::CodeItem* code_item,
uint32_t vA, uint32_t vC) {
uint16_t registers_size = code_item->registers_size_;
/*
* vA/vC are unsigned 8-bit/16-bit quantities for /range instructions,
* so there's no risk of integer overflow when adding them here.
*/
if (vA + vC > registers_size) {
LOG(ERROR) << "VFY: invalid reg index " << vA << "+" << vC
<< " in range invoke (> " << registers_size << ")";
return false;
}
return true;
}
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;
uint16_t expected_signature;
uint32_t switch_count, table_size;
int32_t switch_offset, keys_offset, targets_offset;
int32_t offset, abs_offset;
uint32_t targ;
/* make sure the start of the switch is in range */
switch_offset = insns[1] | ((int32_t) insns[2]) << 16;
if ((int32_t) cur_offset + switch_offset < 0 ||
cur_offset + switch_offset + 2 >= insn_count) {
LOG(ERROR) << "VFY: invalid switch start: at " << cur_offset
<< ", switch offset " << switch_offset << ", count "
<< insn_count;
return false;
}
/* offset to switch table is a relative branch-style offset */
switch_insns = insns + switch_offset;
/* make sure the table is 32-bit aligned */
if ((((uint32_t) switch_insns) & 0x03) != 0) {
LOG(ERROR) << "VFY: unaligned switch table: at " << cur_offset
<< ", switch offset " << switch_offset;
return false;
}
switch_count = switch_insns[1];
if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
/* 0=sig, 1=count, 2/3=firstKey */
targets_offset = 4;
keys_offset = -1;
expected_signature = Instruction::kPackedSwitchSignature;
} else {
/* 0=sig, 1=count, 2..count*2 = keys */
keys_offset = 2;
targets_offset = 2 + 2 * switch_count;
expected_signature = Instruction::kSparseSwitchSignature;
}
table_size = targets_offset + switch_count * 2;
if (switch_insns[0] != expected_signature) {
LOG(ERROR) << "VFY: wrong signature for switch table (0x" << std::hex
<< switch_insns[0] << ", wanted 0x" << expected_signature << ")"
<< std::dec;
return false;
}
/* make sure the end of the switch is in range */
if (cur_offset + switch_offset + table_size > (uint32_t) insn_count) {
LOG(ERROR) << "VFY: invalid switch end: at " << cur_offset
<< ", switch offset " << switch_offset << ", end "
<< cur_offset + switch_offset + table_size << ", count "
<< insn_count;
return false;
}
/* for a sparse switch, verify the keys are in ascending order */
if (keys_offset > 0 && switch_count > 1) {
int32_t last_key;
last_key = switch_insns[keys_offset] |
(switch_insns[keys_offset + 1] << 16);
for (targ = 1; targ < switch_count; targ++) {
int32_t key = (int32_t) switch_insns[keys_offset + targ * 2] |
(int32_t) (switch_insns[keys_offset + targ * 2 + 1] << 16);
if (key <= last_key) {
LOG(ERROR) << "VFY: invalid packed switch: last key=" << last_key
<< ", this=" << key;
return false;
}
last_key = key;
}
}
/* verify each switch target */
for (targ = 0; targ < switch_count; targ++) {
offset = (int32_t) switch_insns[targets_offset + targ * 2] |
(int32_t) (switch_insns[targets_offset + targ * 2 + 1] << 16);
abs_offset = cur_offset + offset;
if (abs_offset < 0 || abs_offset >= (int32_t) insn_count ||
!InsnIsOpcode(insn_flags, abs_offset)) {
LOG(ERROR) << "VFY: invalid switch target " << offset << " (-> "
<< std::hex << abs_offset << ") at " << cur_offset << std::dec
<< "[" << targ << "]";
return false;
}
InsnSetBranchTarget(insn_flags, abs_offset);
}
return true;
}
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;
if (!GetBranchOffset(code_item, insn_flags, cur_offset, &offset,
&isConditional, &selfOkay))
return false;
if (!selfOkay && offset == 0) {
LOG(ERROR) << "VFY: branch offset of zero not allowed at" << std::hex
<< cur_offset << std::dec;
return false;
}
/*
* Check for 32-bit overflow. This isn't strictly necessary if we can
* depend on the VM to have identical "wrap-around" behavior, but
* 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 " << std::hex << cur_offset
<< std::dec << " +" << offset;
return false;
}
abs_offset = cur_offset + offset;
if (abs_offset < 0 || (uint32_t) abs_offset >= insn_count ||
!InsnIsOpcode(insn_flags, abs_offset))
{
LOG(ERROR) << "VFY: invalid branch target " << offset << " (-> "
<< std::hex << abs_offset << ") at " << cur_offset << std::dec;
return false;
}
InsnSetBranchTarget(insn_flags, abs_offset);
return true;
}
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_.get();
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_.reset(new RegisterLine[insns_size]());
DCHECK_GT(insns_size, 0U);
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;
}
addr += inst->Size();
inst = inst->Next();
}
CHECK_EQ(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] == '[' && descriptor[1] != '\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) {
DCHECK_EQ(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) {
DCHECK_EQ(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_.get();
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;
DCHECK_GE(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());
DCHECK_EQ(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;
}
int DexVerifier::SetUninitInstance(UninitInstanceMap* uninit_map, int addr,
Class* klass) {
int idx;
DCHECK(klass != NULL);
/* TODO: binary search when num_entries > 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(FATAL) << "VFY: addr " << addr << " not found in uninit map";
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_.get();
const uint16_t* insns = code_item->insns_;
uint32_t insns_size = code_item->insns_size_;
size_t insn_idx, start_guess;
/* Begin by marking the first instruction as "changed". */
InsnSetChanged(insn_flags, 0, true);
start_guess = 0;
/* 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 (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 = &reg_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_.get() != NULL && CompareLineToTable(reg_table,
insn_idx, &reg_table->work_line_) != 0) {
LOG(ERROR) << "HUH? work_line diverged in " << PrettyMethod(method);
}
#endif
}
if (!CodeFlowVerifyInstruction(vdata, reg_table, insn_idx, &start_guess)) {
LOG(ERROR) << "VFY: failure to verify " << PrettyMethod(method);
return false;
}
/* Clear "changed" and mark as visited. */
InsnSetVisited(insn_flags, insn_idx, true);
InsnSetChanged(insn_flags, insn_idx, false);
}
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) {
LOG(INFO) << "VFY: dead code 0x" << std::hex << dead_start << "-"
<< insn_idx - 1 << std::dec << " in " << PrettyMethod(method);
dead_start = -1;
}
}
if (dead_start >= 0) {
LOG(INFO) << "VFY: dead code 0x" << std::hex << dead_start << "-"
<< insn_idx - 1 << std::dec << " in " << PrettyMethod(method);
}
}
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_.get();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
UninitInstanceMap* uninit_map = vdata->uninit_map_.get();
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 = &reg_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(&reg_table->saved_line_, work_line,
reg_table->insn_reg_count_plus_);
} else {
#ifndef NDEBUG
memset(reg_table->saved_line_.reg_types_.get(), 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) {
DCHECK(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 */
DCHECK(return_type != kRegTypeZero);
DCHECK(!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() &&
!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 = ResolveClassAndCheckAccess(dex_file, dec_insn.vB_, klass, &failure);
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();
DCHECK(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_.get() != 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 = ResolveClassAndCheckAccess(dex_file, dec_insn.vB_, klass, &failure);
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();
DCHECK(failure != VERIFY_ERROR_GENERIC);
} else {
RegType 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 = ResolveClassAndCheckAccess(dex_file, dec_insn.vC_, klass, &failure);
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();
DCHECK(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 = ResolveClassAndCheckAccess(dex_file, dec_insn.vB_, klass, &failure);
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();
DCHECK(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);
DCHECK_GE(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 = ResolveClassAndCheckAccess(dex_file, dec_insn.vC_, klass, &failure);
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();
DCHECK(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 = ResolveClassAndCheckAccess(dex_file, dec_insn.vB_, klass, &failure);
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();
DCHECK(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() ||
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);
DCHECK(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() || 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() || 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;
DCHECK(res_class != NULL);
if (!res_class->IsArrayClass()) {
LOG(ERROR) << "VFY: aget-object on non-array class";
failure = VERIFY_ERROR_GENERIC;
break;
}
DCHECK(res_class->GetComponentType() != NULL);
/*
* Find the element class.
*/
element_class = res_class->GetComponentType();
if (element_class->IsPrimitive()) {
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() || 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() || 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.
*
* 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[].
*/
element_class = array_class->GetComponentType();
if (element_class->IsPrimitive()) {
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) {
LOG(ERROR) << "VFY: invalid iget-1nr of " << PrettyField(inst_field)
<< " (inst=" << tmp_type << " field=" << field_type << ")";
failure = VERIFY_ERROR_GENERIC;
break;
}
SetRegisterType(work_line, dec_insn.vA_, tmp_type);
}
break;
case Instruction::IGET_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;
/* check the type, which should be prim */
switch (inst_field->GetType()->GetPrimitiveType()) {
case Class::kPrimDouble:
SetRegisterType(work_line, dec_insn.vA_, kRegTypeDoubleLo);
break;
case Class::kPrimLong:
SetRegisterType(work_line, dec_insn.vA_, kRegTypeLongLo);
break;
default:
LOG(ERROR) << "VFY: invalid iget-wide of " << PrettyField(inst_field);
failure = VERIFY_ERROR_GENERIC;
break;
}
}
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;
}
DCHECK(!field_class->IsPrimitive()) << PrettyClass(field_class);
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) {
LOG(ERROR) << "VFY: invalid iput-1nr of " << PrettyField(inst_field)
<< " (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 " << PrettyField(inst_field);
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