blob: 2908d580f367086ce3d17d3428ea706fc30b1558 [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(work_line, dec_insn.vB_);
inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
if (failure != VERIFY_ERROR_NONE)
break;
DCHECK(inst_field != NULL);
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)) {
LOG(ERROR) << "VFY: storing type '"
<< value_class->GetDescriptor()->ToModifiedUtf8()
<< "' into field type '"
<< field_class->GetDescriptor()->ToModifiedUtf8()
<< "' (" << PrettyField(inst_field) << ")";
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) {
LOG(ERROR) << "VFY: invalid sget-1nr of " << PrettyField(static_field)
<< " (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 = GetStaticField(vdata, dec_insn.vB_, &failure);
if (failure != VERIFY_ERROR_NONE)
break;
/* check the type, which should be prim */
switch (static_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 sget-wide of " << PrettyField(static_field);
failure = VERIFY_ERROR_GENERIC;
break;
}
}
break;
case Instruction::SGET_OBJECT:
{
Field* static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
if (failure != VERIFY_ERROR_NONE)
break;
Class* 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) {
LOG(ERROR) << "VFY: invalid sput-1nr of " << PrettyField(static_field)
<< " (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 " << PrettyField(static_field);
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)) {
LOG(ERROR) << "VFY: storing type '"
<< value_class->GetDescriptor()->ToModifiedUtf8()
<< "' into field type '"
<< field_class->GetDescriptor()->ToModifiedUtf8()
<< "' (" << PrettyField(static_field) << ")";
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);
DCHECK(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);
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);
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:
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) {
/* 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_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_.get() != 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;
}
DCHECK_EQ(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 */
DCHECK((*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;
DCHECK_LT(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_,
&reg_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 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;
}
DCHECK_LT(*start_guess, insns_size);
DCHECK_NE(InsnGetWidth(insn_flags, *start_guess), 0);
return true;
}
bool DexVerifier::ReplaceFailingInstruction(const DexFile::CodeItem* code_item,
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;
}
DCHECK(inst->IsThrow());
/* write a NOP over the third code unit, if necessary */
int width = inst->Size();
switch (width) {
case 2:
/* nothing to do */
break;
case 3:
UpdateCodeUnit(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 */
DCHECK(width == 2 || width == 3);
uint16_t new_val = Instruction::THROW_VERIFICATION_ERROR |
(failure << 8) | (ref_type << (8 + kVerifyErrorRefTypeShift));
UpdateCodeUnit(insns, new_val);
return true;
}
void DexVerifier::UpdateCodeUnit(const uint16_t* ptr, uint16_t new_val) {
*(uint16_t*) ptr = new_val;
}
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_.get() == 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;
}
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_.get() == 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_.get();
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 NULL;
}
Field* field = ResolveFieldAndCheckAccess(dex_file, field_idx,
method->GetDeclaringClass(), failure, false);
if (field == NULL) {
LOG(ERROR) << "VFY: unable to resolve instance field " << field_idx;
return NULL;
}
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.
*/
Class* obj_class = RegTypeReferenceToClass(obj_type, uninit_map);
DCHECK(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 (!field->GetDeclaringClass()->IsAssignableFrom(obj_class)) {
LOG(ERROR) << "VFY: invalid field access (field " << PrettyField(field)
<< ", 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_;
Field* field = ResolveFieldAndCheckAccess(dex_file, field_idx,
method->GetDeclaringClass(), failure, 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);
*failure = VERIFY_ERROR_NO_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);
VerifyError local_failure;
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()) {
DexFile::CatchHandlerItem handler = iterator.Get();
if (handler.address_ == (uint32_t) insn_idx) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Class* klass;
if (handler.type_idx_ == DexFile::kDexNoIndex) {
klass = class_linker->FindSystemClass("Ljava/lang/Throwable;");
} else {
klass = ResolveClassAndCheckAccess(dex_file, handler.type_idx_,
method->GetDeclaringClass(), &local_failure);
}
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();
}
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;
}
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);
}
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 "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;
}
return RegTypeInitializedReferenceToClass(type);
}
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;
}
/* 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;
}
void DexVerifier::SetRegisterType(RegisterLine* register_line, uint32_t vdst,
RegType new_type) {
RegType* insn_regs = register_line->reg_types_.get();
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 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;
}
/*
* Clear the monitor entry bits for this register.
*/
if (register_line->monitor_entries_.get() != 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_.get();
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 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 kRegTypeConstLo:
case kRegTypeConstHi:
case kRegTypeLongHi:
case kRegTypeDoubleHi:
case kRegTypeZero:
case kRegTypeOne:
case kRegTypeUnknown:
case kRegTypeConflict:
/* should never be checking for these explicitly */
DCHECK(false);
*failure = VERIFY_ERROR_GENERIC;
return;
case kRegTypeUninit:
default:
/* make sure check_type is initialized reference */
if (!RegTypeIsReference(check_type)) {
LOG(FATAL) << "VFY: unexpected check type " << check_type;
*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);
DCHECK(src_class != NULL);
DCHECK(check_class != NULL);
if (check_class->IsInterface()) {
/*
* All objects implement all interfaces as far as the verifier is
* concerned. The runtime has to sort it out. See coments above
* FindCommonSuperclass.
*/
} else {
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;
}
}
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_.get();
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;
}
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++;
}
}
DCHECK_GT(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_.get();
for (int i = 0; i < insn_reg_count; i++) {
if (insn_regs[i] == uninit_type) {
insn_regs[i] = kRegTypeConflict;
if (register_line->monitor_entries_.get() != NULL)
register_line->monitor_entries_[i] = 0;
}
}
}
void DexVerifier::CopyRegister1(RegisterLine* register_line, uint32_t vdst,
uint32_t vsrc, TypeCategory cat, VerifyError* failure) {
DCHECK(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_.get() != 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) {
DCHECK_LT(vdst, static_cast<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) {
DCHECK_LT(vdst, static_cast<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();
DCHECK(c1 != NULL);
DCHECK(c2 != NULL);
}
return c1;
}
Class* DexVerifier::FindCommonArraySuperclass(Class* c1, Class* c2) {
DCHECK(c1->IsArrayClass());
DCHECK(c2->IsArrayClass());
Class* e1 = c1->GetComponentType();
Class* e2 = c2->GetComponentType();
if (e1->IsPrimitive() || e2->IsPrimitive()) {
return c1->GetSuperClass(); // == java.lang.Object
}
Class* common_elem = FindCommonSuperclass(c1->GetComponentType(), c2->GetComponentType());
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
const ClassLoader* class_loader = c1->GetClassLoader();
std::string descriptor = "[" + common_elem->GetDescriptor()->ToModifiedUtf8();
Class* array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
DCHECK(array_class != NULL);
return array_class;
}
Class* DexVerifier::FindCommonSuperclass(Class* c1, Class* c2) {
DCHECK(!c1->IsPrimitive()) << PrettyClass(c1);
DCHECK(!c2->IsPrimitive()) << PrettyClass(c2);
if (c1 == c2)
return c1;
if (c1->IsInterface() && c1->IsAssignableFrom(c2)) {
return c1;
}
if (c2->IsInterface() && c2->IsAssignableFrom(c1)) {
return c2;
}
if (c1->IsArrayClass() && c2->IsArrayClass()) {
return FindCommonArraySuperclass(c1, c2);
}
return DigForSuperclass(c1, c2);
}
Class* DexVerifier::ResolveClassAndCheckAccess(const DexFile* dex_file,
uint32_t class_idx, const Class* referrer, VerifyError* failure) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Class* res_class = class_linker->ResolveType(*dex_file, class_idx, referrer);
if (res_class == NULL) {
Thread::Current()->ClearException();
LOG(ERROR) << "VFY: can't find class with index 0x" << std::hex << class_idx << std::dec;
*failure = VERIFY_ERROR_NO_CLASS;
return NULL;
}
/* Check if access is allowed. */
if (!referrer->CanAccess(res_class)) {
LOG(ERROR) << "VFY: illegal class access: "
<< referrer->GetDescriptor()->ToModifiedUtf8() << " -> "
<< res_class->GetDescriptor()->ToModifiedUtf8();
*failure = VERIFY_ERROR_ACCESS_CLASS;
return NULL;
}
return res_class;
}
Method* DexVerifier::ResolveMethodAndCheckAccess(const DexFile* dex_file,
uint32_t method_idx, const Class* referrer, VerifyError* failure,
bool is_direct) {
DexCache* dex_cache = referrer->GetDexCache();
Method* res_method = dex_cache->GetResolvedMethod(method_idx);
if (res_method == NULL) {
const DexFile::MethodId& method_id = dex_file->GetMethodId(method_idx);
Class* klass = ResolveClassAndCheckAccess(dex_file, method_id.class_idx_, referrer, failure);
if (klass == NULL) {
DCHECK(*failure != VERIFY_ERROR_NONE);
return NULL;
}
const char* name = dex_file->dexStringById(method_id.name_idx_);
std::string signature(dex_file->CreateMethodDescriptor(method_id.proto_idx_, NULL));
if (is_direct) {
res_method = klass->FindDirectMethod(name, signature);
} else if (klass->IsInterface()) {
res_method = klass->FindInterfaceMethod(name, signature);
} else {
res_method = klass->FindVirtualMethod(name, signature);
}
if (res_method != NULL) {
dex_cache->SetResolvedMethod(method_idx, res_method);
} else {
LOG(ERROR) << "VFY: couldn't find method "
<< klass->GetDescriptor()->ToModifiedUtf8() << "." << name
<< " " << signature;
*failure = VERIFY_ERROR_NO_METHOD;
return NULL;
}
}
/* Check if access is allowed. */
if (!referrer->CanAccessMember(res_method->GetDeclaringClass(), res_method->GetAccessFlags())) {
LOG(ERROR) << "VFY: illegal method access (call " << PrettyMethod(res_method)
<< " from " << referrer->GetDescriptor()->ToModifiedUtf8() << ")";
*failure = VERIFY_ERROR_ACCESS_METHOD;
return NULL;
}
return res_method;
}
Field* DexVerifier::ResolveFieldAndCheckAccess(const DexFile* dex_file,
uint32_t field_idx, const Class* referrer, VerifyError* failure,
bool is_static) {
DexCache* dex_cache = referrer->GetDexCache();
Field* res_field = dex_cache->GetResolvedField(field_idx);
if (res_field == NULL) {
const DexFile::FieldId& field_id = dex_file->GetFieldId(field_idx);
Class* klass = ResolveClassAndCheckAccess(dex_file, field_id.class_idx_, referrer, failure);
if (klass == NULL) {
DCHECK(*failure != VERIFY_ERROR_NONE) << PrettyClass(referrer);
return NULL;
}
Class* field_type = ResolveClassAndCheckAccess(dex_file, field_id.type_idx_, referrer, failure);
if (field_type == NULL) {
DCHECK(*failure != VERIFY_ERROR_NONE) << PrettyClass(referrer) << " " << PrettyClass(klass);
return NULL;
}
const char* name = dex_file->dexStringById(field_id.name_idx_);
if (is_static) {
res_field = klass->FindStaticField(name, field_type);
} else {
res_field = klass->FindInstanceField(name, field_type);
}
if (res_field != NULL) {
dex_cache->SetResolvedField(field_idx, res_field);
} else {
LOG(ERROR) << "VFY: couldn't find field "
<< klass->GetDescriptor()->ToModifiedUtf8() << "." << name;
*failure = VERIFY_ERROR_NO_FIELD;
return NULL;
}
}
/* Check if access is allowed. */
if (!referrer->CanAccessMember(res_field->GetDeclaringClass(), res_field->GetAccessFlags())) {
LOG(ERROR) << "VFY: access denied from "
<< referrer->GetDescriptor()->ToModifiedUtf8() << " to field "
<< PrettyField(res_field);
*failure = VERIFY_ERROR_ACCESS_FIELD;
return NULL;
}
return res_field;
}
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);
DCHECK(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_;
DCHECK(work_line != NULL);
const RegType* work_regs = work_line->reg_types_.get();
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_.get();
MonitorEntries* work_mon_ents = work_line->monitor_entries_.get();
MonitorEntries* target_mon_ents = target_line->monitor_entries_.get();
bool changed = false;
unsigned int idx;
DCHECK(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_.get(), work_line->monitor_stack_.get(),
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 },
};
DCHECK(check_type >= kRegType1nrSTART);
DCHECK(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) {
DCHECK(field != NULL);
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 " << PrettyField(field);
*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_.get();
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:
DCHECK(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) {
DCHECK(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) {
DCHECK(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) {
DCHECK(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) {
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! */
DCHECK(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;
DCHECK(res_class->IsArrayClass()) << PrettyClass(res_class);
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_.get();
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 = ResolveMethodAndCheckAccess(dex_file, dec_insn->vB_, method->GetDeclaringClass(),
failure, (method_type == METHOD_DIRECT || method_type == METHOD_STATIC));
if (res_method == NULL) {
const DexFile::MethodId& method_id = dex_file->GetMethodId(dec_insn->vB_);
const char* method_name = dex_file->GetMethodName(method_id);
const char* method_proto = dex_file->GetMethodPrototype(method_id);
const char* class_descriptor = dex_file->GetMethodClassDescriptor(method_id);
LOG(ERROR) << "VFY: unable to resolve method " << dec_insn->vB_ << ": "
<< class_descriptor << "." << method_name << " " << method_proto;
return NULL;
}
/*
* Make sure calls to "<init>" are "direct". There are additional restrictions
* but we don't enfore them here.
*/
if (res_method->GetName()->Equals("<init>") && method_type != METHOD_DIRECT) {
LOG(ERROR) << "VFY: invalid call to " << PrettyMethod(res_method);
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 "
<< PrettyMethod(res_method);
*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) {
DCHECK(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 " << PrettyMethod(method)
<< " to super -." << res_method->GetName()->ToModifiedUtf8()
<< " " << res_method->GetSignature()->ToModifiedUtf8();
} else {
LOG(ERROR) << "VFY: invalid invoke-super from " << PrettyMethod(method)
<< " 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 */
DCHECK(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++) {
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:
LOG(ERROR) << "VFY: rejecting call to " << PrettyMethod(res_method);
if (*failure == VERIFY_ERROR_NONE)
*failure = VERIFY_ERROR_GENERIC;
return NULL;
}
DexVerifier::RegisterMap* DexVerifier::GenerateRegisterMapV(VerifierData* vdata)
{
const DexFile::CodeItem* code_item = vdata->code_item_;
int i, bytes_for_addr, gc_point_count;
if (code_item->registers_size_ >= 2048) {
LOG(ERROR) << "ERROR: register map can't handle "
<< code_item->registers_size_ << " registers";
return NULL;
}
uint8_t reg_width = (code_item->registers_size_ + 7) / 8;
/*
* Decide if we need 8 or 16 bits to hold the address. Strictly speaking
* we only need 16 bits if we actually encode an address >= 256 -- if
* the method has a section at the end without GC points (e.g. array
* data) we don't need to count it. The situation is unusual, and
* detecting it requires scanning the entire method, so we don't bother.
*/
RegisterMapFormat format;
if (code_item->insns_size_ < 256) {
format = kRegMapFormatCompact8;
bytes_for_addr = 1;
} else {
format = kRegMapFormatCompact16;
bytes_for_addr = 2;
}
/*
* Count up the number of GC point instructions.
*
* NOTE: this does not automatically include the first instruction,
* since we don't count method entry as a GC point.
*/
gc_point_count = 0;
for (i = 0; i < (int) code_item->insns_size_; i++) {
if (InsnIsGcPoint(vdata->insn_flags_.get(), i))
gc_point_count++;
}
if (gc_point_count >= 65536) {
/* We could handle this, but in practice we don't get near this. */
LOG(ERROR) << "ERROR: register map can't handle " << gc_point_count
<< " gc points in one method";
return NULL;
}
/* Calculate size of buffer to hold the map data. */
uint32_t data_size = gc_point_count * (bytes_for_addr + reg_width);
RegisterMap* map = new RegisterMap(format, reg_width, gc_point_count,
data_size);
/* Populate it. */
uint8_t* map_data = map->data_;
for (i = 0; i < (int) vdata->code_item_->insns_size_; i++) {
if (InsnIsGcPoint(vdata->insn_flags_.get(), i)) {
DCHECK(vdata->register_lines_[i].reg_types_.get() != NULL);
if (format == kRegMapFormatCompact8) {
*map_data++ = i;
} else /*kRegMapFormatCompact16*/ {
*map_data++ = i & 0xff;
*map_data++ = i >> 8;
}
OutputTypeVector(vdata->register_lines_[i].reg_types_.get(),
code_item->registers_size_, map_data);
map_data += reg_width;
}
}
DCHECK_EQ((uint32_t) map_data - (uint32_t) map->data_, data_size);
// TODO: Remove this check when it's really running...
#if 1
if (!VerifyMap(vdata, map)) {
LOG(ERROR) << "Map failed to verify";
return NULL;
}
#endif
/* Try to compress the map. */
#if 0
RegisterMap* compress_map = CompressMapDifferential(map);
if (compress_map != NULL) {
// TODO: Remove this check when it's really running...
#if 1
/*
* Expand the compressed map we just created, and compare it
* to the original. Abort the VM if it doesn't match up.
*/
UniquePtr<RegisterMap> uncompressed_map(UncompressMapDifferential(compress_map));
if (uncompressed_map.get() == NULL) {
LOG(ERROR) << "Map failed to uncompress - " << PrettyMethod(vdata->method_);
delete map;
delete compress_map;
/* bad - compression is broken or we're out of memory */
return NULL;
} else {
if (!CompareMaps(map, uncompressed_map.get())) {
LOG(ERROR) << "Map comparison failed - " << PrettyMethod(vdata->method_);
delete map;
delete compress_map;
/* bad - compression is broken */
return NULL;
}
}
#endif
delete map;
map = compress_map;
}
#endif
return map;
}
DexVerifier::RegisterMap* DexVerifier::GetExpandedRegisterMapHelper(
Method* method, RegisterMap* map) {
RegisterMap* new_map;
if (map == NULL)
return NULL;
/* TODO: sanity check to ensure this isn't called w/o external locking */
uint8_t format = map->header_->format_;
switch (format) {
case kRegMapFormatCompact8:
case kRegMapFormatCompact16:
/* already expanded */
return map;
case kRegMapFormatDifferential:
new_map = UncompressMapDifferential(map);
break;
default:
LOG(ERROR) << "Unknown format " << format
<< " in dvmGetExpandedRegisterMap";
return NULL;
}
if (new_map == NULL) {
LOG(ERROR) << "Map failed to uncompress (fmt=" << format << ") "
<< PrettyMethod(method);
return NULL;
}
/* Update method, and free compressed map if it was sitting on the heap. */
//ByteArray* header = ByteArray::Alloc(sizeof(RegisterMapHeader));
//ByteArray* data = ByteArray::Alloc(ComputeRegisterMapSize(map));
//memcpy(header->GetData(), map->header_, sizeof(RegisterMapHeader));
//memcpy(data->GetData(), map->data_, ComputeRegisterMapSize(map));
//method->SetRegisterMapHeader(header);
//method->SetRegisterMapData(data);
delete map;
return new_map;
}
const uint8_t* DexVerifier::RegisterMapGetLine(const RegisterMap* map, int addr) {
int addr_width, line_width;
uint8_t format = map->header_->format_;
uint16_t num_entries = map->header_->num_entries_;
DCHECK_GT(num_entries, 0);
switch (format) {
case kRegMapFormatNone:
return NULL;
case kRegMapFormatCompact8:
addr_width = 1;
break;
case kRegMapFormatCompact16:
addr_width = 2;
break;
default:
LOG(ERROR) << "Unknown format " << format;
return NULL;
}
line_width = addr_width + map->header_->reg_width_;
/*
* Find the appropriate entry. Many maps are very small, some are very large.
*/
static const int kSearchThreshold = 8;
const uint8_t* data = NULL;
int line_addr;
if (num_entries < kSearchThreshold) {
int i;
data = map->data_;
for (i = num_entries; i > 0; i--) {
line_addr = data[0];
if (addr_width > 1)
line_addr |= data[1] << 8;
if (line_addr == addr)
return data + addr_width;
data += line_width;
}
DCHECK_EQ(data, map->data_ + line_width * num_entries);
} else {
int hi, lo, mid;
lo = 0;
hi = num_entries -1;
while (hi >= lo) {
mid = (hi + lo) / 2;
data = map->data_ + line_width * mid;
line_addr = data[0];
if (addr_width > 1)
line_addr |= data[1] << 8;
if (addr > line_addr) {
lo = mid + 1;
} else if (addr < line_addr) {
hi = mid - 1;
} else {
return data + addr_width;
}
}
}
return NULL;
}
void DexVerifier::OutputTypeVector(const RegType* regs, int insn_reg_count,
uint8_t* data) {
uint8_t val = 0;
int i;
for (i = 0; i < insn_reg_count; i++) {
RegType type = *regs++;
val >>= 1;
if (IsReferenceType(type))
val |= 0x80; /* set hi bit */
if ((i & 0x07) == 7)
*data++ = val;
}
if ((i & 0x07) != 0) {
/* Flush bits from last byte. */
val >>= 8 - (i & 0x07);
*data++ = val;
}
}
bool DexVerifier::VerifyMap(VerifierData* vdata, const RegisterMap* map) {
const uint8_t* raw_map = map->data_;
uint8_t format = map->header_->format_;
const int num_entries = map->header_->num_entries_;
int ent;
if ((vdata->code_item_->registers_size_ + 7) / 8 != map->header_->reg_width_) {
LOG(ERROR) << "GLITCH: registersSize=" << vdata->code_item_->registers_size_
<< ", reg_width=" << map->header_->reg_width_;
return false;
}
for (ent = 0; ent < num_entries; ent++) {
int addr;
switch (format) {
case kRegMapFormatCompact8:
addr = *raw_map++;
break;
case kRegMapFormatCompact16:
addr = *raw_map++;
addr |= (*raw_map++) << 8;
break;
default:
LOG(FATAL) << "GLITCH: bad format (" << format << ")";
return false;
}
const RegType* regs = vdata->register_lines_[addr].reg_types_.get();
if (regs == NULL) {
LOG(ERROR) << "GLITCH: addr " << addr << " has no data";
return false;
}
uint8_t val = 0;
int i;
for (i = 0; i < vdata->code_item_->registers_size_; i++) {
bool bit_is_ref, reg_is_ref;
val >>= 1;
if ((i & 0x07) == 0) {
/* Load next byte of data. */
val = *raw_map++;
}
bit_is_ref = val & 0x01;
RegType type = regs[i];
reg_is_ref = IsReferenceType(type);
if (bit_is_ref != reg_is_ref) {
LOG(ERROR) << "GLITCH: addr " << addr << " reg " << i << ": bit="
<< bit_is_ref << " reg=" << reg_is_ref << "(" << type << ")";
return false;
}
}
/* Raw_map now points to the address field of the next entry. */
}
return true;
}
bool DexVerifier::CompareMaps(const RegisterMap* map1, const RegisterMap* map2)
{
size_t size1, size2;
size1 = ComputeRegisterMapSize(map1);
size2 = ComputeRegisterMapSize(map2);
if (size1 != size2) {
LOG(ERROR) << "CompareMaps: size mismatch (" << size1 << " vs " << size2
<< ")";
return false;
}
if (map1->header_->format_ != map2->header_->format_ ||
map1->header_->reg_width_ != map2->header_->reg_width_ ||
map1->header_->num_entries_ != map2->header_->num_entries_) {
LOG(ERROR) << "CompareMaps: fields mismatch";
}
if (memcmp(map1->data_, map2->data_, size1) != 0) {
LOG(ERROR) << "CompareMaps: data mismatch";
return false;
}
return true;
}
size_t DexVerifier::ComputeRegisterMapSize(const RegisterMap* map) {
uint8_t format = map->header_->format_;
uint16_t num_entries = map->header_->num_entries_;
DCHECK(map != NULL);
switch (format) {
case kRegMapFormatNone:
return 1;
case kRegMapFormatCompact8:
return (1 + map->header_->reg_width_) * num_entries;
case kRegMapFormatCompact16:
return (2 + map->header_->reg_width_) * num_entries;
case kRegMapFormatDifferential:
{
/* Decoded ULEB128 length. */
const uint8_t* ptr = map->data_;
return DecodeUnsignedLeb128(&ptr);
}
default:
LOG(FATAL) << "Bad register map format " << format;
return 0;
}
}
int DexVerifier::ComputeBitDiff(const uint8_t* bits1, const uint8_t* bits2,
int byte_width, int* first_bit_changed_ptr, int* num_bits_changed_ptr,
uint8_t* leb_out_buf) {
int num_bits_changed = 0;
int first_bit_changed = -1;
int leb_size = 0;
int byte_num;
/*
* Run through the vectors, first comparing them at the byte level. This
* will yield a fairly quick result if nothing has changed between them.
*/
for (byte_num = 0; byte_num < byte_width; byte_num++) {
uint8_t byte1 = *bits1++;
uint8_t byte2 = *bits2++;
if (byte1 != byte2) {
/* Walk through the byte, identifying the changed bits. */
int bit_num;
for (bit_num = 0; bit_num < 8; bit_num++) {
if (((byte1 >> bit_num) & 0x01) != ((byte2 >> bit_num) & 0x01)) {
int bit_offset = (byte_num << 3) + bit_num;
if (first_bit_changed < 0)
first_bit_changed = bit_offset;
num_bits_changed++;
if (leb_out_buf == NULL) {
leb_size += UnsignedLeb128Size(bit_offset);
} else {
uint8_t* cur_buf = leb_out_buf;
leb_out_buf = WriteUnsignedLeb128(leb_out_buf, bit_offset);
leb_size += leb_out_buf - cur_buf;
}
}
}
}
}
if (num_bits_changed > 0) {
DCHECK_GE(first_bit_changed, 0);
}
if (first_bit_changed_ptr != NULL) {
*first_bit_changed_ptr = first_bit_changed;
}
if (num_bits_changed_ptr != NULL) {
*num_bits_changed_ptr = num_bits_changed;
}
return leb_size;
}
DexVerifier::RegisterMap* DexVerifier::CompressMapDifferential(
const RegisterMap* map) {
int orig_size = ComputeRegisterMapSize(map);
uint8_t* tmp_ptr;
int addr_width;
uint8_t format = map->header_->format_;
switch (format) {
case kRegMapFormatCompact8:
addr_width = 1;
break;
case kRegMapFormatCompact16:
addr_width = 2;
break;
default:
LOG(ERROR) << "ERROR: can't compress map with format=" << format;
return NULL;
}
int reg_width = map->header_->reg_width_;
int num_entries = map->header_->num_entries_;
if (num_entries <= 1) {
return NULL;
}
/*
* We don't know how large the compressed data will be. It's possible
* for it to expand and become larger than the original. The header
* itself is variable-sized, so we generate everything into a temporary
* buffer and then copy it to form-fitting storage once we know how big
* it will be (and that it's smaller than the original).
*
* If we use a size that is equal to the size of the input map plus
* a value longer than a single entry can possibly expand to, we need
* only check for overflow at the end of each entry. The worst case
* for a single line is (1 + <ULEB8 address> + <full copy of vector>).
* Addresses are 16 bits, so that's (1 + 3 + reg_width).
*
* The initial address offset and bit vector will take up less than
* or equal to the amount of space required when uncompressed -- large
* initial offsets are rejected.
*/
UniquePtr<uint8_t[]> tmp_buf(new uint8_t[orig_size + (1 + 3 + reg_width)]);
tmp_ptr = tmp_buf.get();
const uint8_t* map_data = map->data_;
const uint8_t* prev_bits;
uint16_t addr, prev_addr;
addr = *map_data++;
if (addr_width > 1)
addr |= (*map_data++) << 8;
if (addr >= 128) {
LOG(ERROR) << "Can't compress map with starting address >= 128";
return NULL;
}
/*
* Start by writing the initial address and bit vector data. The high
* bit of the initial address is used to indicate the required address
* width (which the decoder can't otherwise determine without parsing
* the compressed data).
*/
*tmp_ptr++ = addr | (addr_width > 1 ? 0x80 : 0x00);
memcpy(tmp_ptr, map_data, reg_width);
prev_bits = map_data;
prev_addr = addr;
tmp_ptr += reg_width;
map_data += reg_width;
/* Loop over all following entries. */
for (int entry = 1; entry < num_entries; entry++) {
int addr_diff;
uint8_t key;
/* Pull out the address and figure out how to encode it. */
addr = *map_data++;
if (addr_width > 1)
addr |= (*map_data++) << 8;
addr_diff = addr - prev_addr;
DCHECK_GT(addr_diff, 0);
if (addr_diff < 8) {
/* Small difference, encode in 3 bits. */
key = addr_diff -1; /* set 00000AAA */
} else {
/* Large difference, output escape code. */
key = 0x07; /* escape code for AAA */
}
int num_bits_changed, first_bit_changed, leb_size;
leb_size = ComputeBitDiff(prev_bits, map_data, reg_width,
&first_bit_changed, &num_bits_changed, NULL);
if (num_bits_changed == 0) {
/* set B to 1 and CCCC to zero to indicate no bits were changed */
key |= 0x08;
} else if (num_bits_changed == 1 && first_bit_changed < 16) {
/* set B to 0 and CCCC to the index of the changed bit */
key |= first_bit_changed << 4;
} else if (num_bits_changed < 15 && leb_size < reg_width) {
/* set B to 1 and CCCC to the number of bits */
key |= 0x08 | (num_bits_changed << 4);
} else {
/* set B to 1 and CCCC to 0x0f so we store the entire vector */
key |= 0x08 | 0xf0;
}
/*
* Encode output. Start with the key, follow with the address
* diff (if it didn't fit in 3 bits), then the changed bit info.
*/
*tmp_ptr++ = key;
if ((key & 0x07) == 0x07)
tmp_ptr = WriteUnsignedLeb128(tmp_ptr, addr_diff);
if ((key & 0x08) != 0) {
int bit_count = key >> 4;
if (bit_count == 0) {
/* nothing changed, no additional output required */
} else if (bit_count == 15) {
/* full vector is most compact representation */
memcpy(tmp_ptr, map_data, reg_width);
tmp_ptr += reg_width;
} else {
/* write bit indices in ULEB128 format */
(void) ComputeBitDiff(prev_bits, map_data, reg_width,
NULL, NULL, tmp_ptr);
tmp_ptr += leb_size;
}
} else {
/* single-bit changed, value encoded in key byte */
}
prev_bits = map_data;
prev_addr = addr;
map_data += reg_width;
/* See if we've run past the original size. */
if (tmp_ptr - tmp_buf.get() >= orig_size) {
return NULL;
}
}
/*
* Create a RegisterMap with the contents.
*
* TODO: consider using a threshold other than merely ">=". We would
* get poorer compression but potentially use less native heap space.
*/
int new_data_size = tmp_ptr - tmp_buf.get();
int new_map_size = new_data_size + UnsignedLeb128Size(new_data_size);
if (new_map_size >= orig_size) {
return NULL;
}
RegisterMap* new_map = new RegisterMap(kRegMapFormatDifferential, reg_width,
num_entries, new_map_size);
tmp_ptr = new_map->data_;
tmp_ptr = WriteUnsignedLeb128(tmp_ptr, new_data_size);
memcpy(tmp_ptr, tmp_buf.get(), new_data_size);
return new_map;
}
DexVerifier::RegisterMap* DexVerifier::UncompressMapDifferential(
const RegisterMap* map) {
uint8_t format = map->header_->format_;
RegisterMapFormat new_format;
int reg_width, num_entries, new_addr_width, new_data_size;
if (format != kRegMapFormatDifferential) {
LOG(ERROR) << "Not differential (" << format << ")";
return NULL;
}
reg_width = map->header_->reg_width_;
num_entries = map->header_->num_entries_;
/* Get the data size; we can check this at the end. */
const uint8_t* src_ptr = map->data_;
int expected_src_len = DecodeUnsignedLeb128(&src_ptr);
const uint8_t* src_start = src_ptr;
/* Get the initial address and the 16-bit address flag. */
int addr = *src_ptr & 0x7f;
if ((*src_ptr & 0x80) == 0) {
new_format = kRegMapFormatCompact8;
new_addr_width = 1;
} else {
new_format = kRegMapFormatCompact16;
new_addr_width = 2;
}
src_ptr++;
/* Now we know enough to allocate the new map. */
new_data_size = (new_addr_width + reg_width) * num_entries;
RegisterMap* new_map = new RegisterMap(new_format, reg_width, num_entries,
new_data_size);
/* Write the start address and initial bits to the new map. */
uint8_t* dst_ptr = new_map->data_;
*dst_ptr++ = addr & 0xff;
if (new_addr_width > 1)
*dst_ptr++ = (uint8_t) (addr >> 8);
memcpy(dst_ptr, src_ptr, reg_width);
int prev_addr = addr;
const uint8_t* prev_bits = dst_ptr; /* point at uncompressed data */
dst_ptr += reg_width;
src_ptr += reg_width;
/* Walk through, uncompressing one line at a time. */
int entry;
for (entry = 1; entry < num_entries; entry++) {
int addr_diff;
uint8_t key;
key = *src_ptr++;
/* Get the address. */
if ((key & 0x07) == 7) {
/* Address diff follows in ULEB128. */
addr_diff = DecodeUnsignedLeb128(&src_ptr);
} else {
addr_diff = (key & 0x07) +1;
}
addr = prev_addr + addr_diff;
*dst_ptr++ = addr & 0xff;
if (new_addr_width > 1)
*dst_ptr++ = (uint8_t) (addr >> 8);
/* Unpack the bits. */
if ((key & 0x08) != 0) {
int bit_count = (key >> 4);
if (bit_count == 0) {
/* No bits changed, just copy previous. */
memcpy(dst_ptr, prev_bits, reg_width);
} else if (bit_count == 15) {
/* Full copy of bit vector is present; ignore prev_bits. */
memcpy(dst_ptr, src_ptr, reg_width);
src_ptr += reg_width;
} else {
/* Copy previous bits and modify listed indices. */
memcpy(dst_ptr, prev_bits, reg_width);
while (bit_count--) {
int bit_index = DecodeUnsignedLeb128(&src_ptr);
ToggleBit(dst_ptr, bit_index);
}
}
} else {
/* Copy previous bits and modify the specified one. */
memcpy(dst_ptr, prev_bits, reg_width);
/* One bit, from 0-15 inclusive, was changed. */
ToggleBit(dst_ptr, key >> 4);
}
prev_addr = addr;
prev_bits = dst_ptr;
dst_ptr += reg_width;
}
if (dst_ptr - new_map->data_ != new_data_size) {
LOG(ERROR) << "ERROR: output " << dst_ptr - new_map->data_
<< " bytes, expected " << new_data_size;
free(new_map);
return NULL;
}
if (src_ptr - src_start != expected_src_len) {
LOG(ERROR) << "ERROR: consumed " << src_ptr - src_start
<< " bytes, expected " << expected_src_len;
free(new_map);
return NULL;
}
return new_map;
}
/* Dump the register types for the specifed address to the log file. */
void DexVerifier::DumpRegTypes(const VerifierData* vdata,
const RegisterLine* register_line, int addr, const char* addr_name,
const UninitInstanceMap* uninit_map) {
const DexFile::CodeItem* code_item = vdata->code_item_;
uint16_t reg_count = code_item->registers_size_;
uint32_t insns_size = code_item->insns_size_;
const InsnFlags* insn_flags = vdata->insn_flags_.get();
const RegType* addr_regs = register_line->reg_types_.get();
int full_reg_count = reg_count + kExtraRegs;
bool branch_target = InsnIsBranchTarget(insn_flags, addr);
int i;
CHECK(addr >= 0 && addr < (int) insns_size);
int reg_char_size = full_reg_count + (full_reg_count - 1) / 4 + 2 + 1;
char reg_chars[reg_char_size + 1];
memset(reg_chars, ' ', reg_char_size);
reg_chars[0] = '[';
if (reg_count == 0)
reg_chars[1] = ']';
else
reg_chars[1 + (reg_count - 1) + (reg_count -1 ) / 4 + 1] = ']';
reg_chars[reg_char_size] = '\0';
for (i = 0; i < reg_count + kExtraRegs; i++) {
char tch;
switch (addr_regs[i]) {
case kRegTypeUnknown: tch = '.'; break;
case kRegTypeConflict: tch = 'X'; break;
case kRegTypeZero: tch = '0'; break;
case kRegTypeOne: tch = '1'; break;
case kRegTypeBoolean: tch = 'Z'; break;
case kRegTypeConstPosByte: tch = 'y'; break;
case kRegTypeConstByte: tch = 'Y'; break;
case kRegTypeConstPosShort: tch = 'h'; break;
case kRegTypeConstShort: tch = 'H'; break;
case kRegTypeConstChar: tch = 'c'; break;
case kRegTypeConstInteger: tch = 'i'; break;
case kRegTypePosByte: tch = 'b'; break;
case kRegTypeByte: tch = 'B'; break;
case kRegTypePosShort: tch = 's'; break;
case kRegTypeShort: tch = 'S'; break;
case kRegTypeChar: tch = 'C'; break;
case kRegTypeInteger: tch = 'I'; break;
case kRegTypeFloat: tch = 'F'; break;
case kRegTypeConstLo: tch = 'N'; break;
case kRegTypeConstHi: tch = 'n'; break;
case kRegTypeLongLo: tch = 'J'; break;
case kRegTypeLongHi: tch = 'j'; break;
case kRegTypeDoubleLo: tch = 'D'; break;
case kRegTypeDoubleHi: tch = 'd'; break;
default:
if (RegTypeIsReference(addr_regs[i])) {
if (RegTypeIsUninitReference(addr_regs[i]))
tch = 'U';
else
tch = 'L';
} else {
tch = '*';
CHECK(false);
}
break;
}
if (i < reg_count)
reg_chars[1 + i + (i / 4)] = tch;
else
reg_chars[1 + i + (i / 4) + 2] = tch;
}
if (addr == 0 && addr_name != NULL) {
char start = branch_target ? '>' : ' ';
LOG(INFO) << start << addr_name << " " << reg_chars << " mst="
<< register_line->monitor_stack_top_;
} else {
char start = branch_target ? '>' : ' ';
LOG(INFO) << start << "0x" << std::hex << addr << std::dec << " "
<< reg_chars << " mst=" << register_line->monitor_stack_top_;
}
for (i = 0; i < reg_count + kExtraRegs; i++) {
if (RegTypeIsReference(addr_regs[i]) && addr_regs[i] != kRegTypeZero) {
Class* klass = RegTypeReferenceToClass(addr_regs[i], uninit_map);
if (i < reg_count) {
const char* undef = RegTypeIsUninitReference(addr_regs[i]) ? "[U]" : "";
LOG(INFO) << " " << i << ": 0x" << std::hex << addr_regs[i] << std::dec
<< " " << undef << klass->GetDescriptor()->ToModifiedUtf8();
} else {
const char* undef = RegTypeIsUninitReference(addr_regs[i]) ? "[U]" : "";
LOG(INFO) << " RS: 0x" << std::hex << addr_regs[i] << std::dec
<< " " << undef << klass->GetDescriptor()->ToModifiedUtf8();
}
}
}
}
} // namespace art