Merge "Added more checking upon resolution and bytecode rewriting to verifier." into dalvik-dev
diff --git a/src/compiler.cc b/src/compiler.cc
index 454c8f8..71727fd 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -143,6 +143,7 @@
}
void Compiler::VerifyDexFile(const ClassLoader* class_loader, const DexFile& dex_file) {
+ dex_file.ChangePermissions(PROT_READ | PROT_WRITE);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); class_def_index++) {
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
@@ -153,6 +154,7 @@
class_linker->VerifyClass(klass);
CHECK(klass->IsVerified() || klass->IsErroneous());
}
+ dex_file.ChangePermissions(PROT_READ);
}
void Compiler::InitializeClassesWithoutClinit(const ClassLoader* class_loader) {
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 7c54018..924953f 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -70,6 +70,10 @@
}
}
+void DexFile::ChangePermissions(int prot) const {
+ closer_->ChangePermissions(prot);
+}
+
DexFile::Closer::~Closer() {}
DexFile::MmapCloser::MmapCloser(void* addr, size_t length) : addr_(addr), length_(length) {
@@ -80,9 +84,15 @@
PLOG(INFO) << "munmap failed";
}
}
+void DexFile::MmapCloser::ChangePermissions(int prot) {
+ if (mprotect(addr_, length_, prot) != 0) {
+ PLOG(FATAL) << "Failed to change dex file permissions to " << prot;
+ }
+}
DexFile::PtrCloser::PtrCloser(byte* addr) : addr_(addr) {}
DexFile::PtrCloser::~PtrCloser() { delete[] addr_; }
+void DexFile::PtrCloser::ChangePermissions(int prot) {}
const DexFile* DexFile::OpenFile(const std::string& filename,
const std::string& original_location,
@@ -106,7 +116,7 @@
return NULL;
}
size_t length = sbuf.st_size;
- void* addr = mmap(NULL, length, PROT_READ, MAP_SHARED, fd, 0);
+ void* addr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) {
PLOG(ERROR) << "mmap \"" << filename << "\" failed";
close(fd);
diff --git a/src/dex_file.h b/src/dex_file.h
index 3a9d58a..9ebcce2 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -832,11 +832,14 @@
}
}
+ void ChangePermissions(int prot) const;
+
private:
// Helper class to deallocate underlying storage.
class Closer {
public:
virtual ~Closer();
+ virtual void ChangePermissions(int prot) = 0;
};
// Helper class to deallocate mmap-backed .dex files.
@@ -844,6 +847,7 @@
public:
MmapCloser(void* addr, size_t length);
virtual ~MmapCloser();
+ virtual void ChangePermissions(int prot);
private:
void* addr_;
size_t length_;
@@ -854,6 +858,7 @@
public:
PtrCloser(byte* addr);
virtual ~PtrCloser();
+ virtual void ChangePermissions(int prot);
private:
byte* addr_;
};
diff --git a/src/dex_instruction.h b/src/dex_instruction.h
index 3934d7d..e08a1f7 100644
--- a/src/dex_instruction.h
+++ b/src/dex_instruction.h
@@ -32,6 +32,7 @@
k11n, // op vA, #+B
k11x, // op vAA
k10t, // op +AA
+ k20bc, // op AA, kind@BBBB
k20t, // op +AAAA
k22x, // op vAA, vBBBB
k21t, // op vAA, +BBBB
diff --git a/src/dex_instruction_list.h b/src/dex_instruction_list.h
index 009573e..6dc5def 100644
--- a/src/dex_instruction_list.h
+++ b/src/dex_instruction_list.h
@@ -238,7 +238,7 @@
V(0xEA, UNUSED_EA, "unused-ea", k10x, false, kUnknown, 0, kVerifyError) \
V(0xEB, UNUSED_EB, "unused-eb", k10x, false, kUnknown, 0, kVerifyError) \
V(0xEC, UNUSED_EC, "unused-ec", k10x, false, kUnknown, 0, kVerifyError) \
- V(0xED, UNUSED_ED, "unused-ed", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xED, THROW_VERIFICATION_ERROR, "throw-verification-error", k20bc, false, kNone, kThrow, kVerifyNone) \
V(0xEE, UNUSED_EE, "unused-ee", k10x, false, kUnknown, 0, kVerifyError) \
V(0xEF, UNUSED_EF, "unused-ef", k10x, false, kUnknown, 0, kVerifyError) \
V(0xF0, UNUSED_F0, "unused-f0", k10x, false, kUnknown, 0, kVerifyError) \
@@ -264,6 +264,7 @@
V(k11n) \
V(k11x) \
V(k10t) \
+ V(k20bc) \
V(k20t) \
V(k22x) \
V(k21t) \
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index a800f20..57e27e3 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -5,6 +5,7 @@
#include <iostream>
#include "class_linker.h"
+#include "dex_cache.h"
#include "dex_file.h"
#include "dex_instruction.h"
#include "dex_instruction_visitor.h"
@@ -3222,13 +3223,13 @@
break;
case Instruction::SHR_INT_LIT8:
tmp_type = AdjustForRightShift(work_line, dec_insn.vB_, dec_insn.vC_,
- false, &failure);
+ 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, &failure);
+ true);
CheckLitop(work_line, &dec_insn, tmp_type, kRegTypeInteger, false,
&failure);
break;
@@ -3244,8 +3245,7 @@
* which don't generally appear during verification. Because it's
* inserted in the course of verification, we can expect to see it here.
*/
- //case Instruction::THROW_VERIFICATION_ERROR:
- case Instruction::UNUSED_ED:
+ case Instruction::THROW_VERIFICATION_ERROR:
break;
/*
@@ -3373,17 +3373,10 @@
<< (int) dec_insn.opcode_ << " at 0x" << insn_idx << std::dec;
return false;
} else {
- // TODO: CHECK IF THIS WILL WORK!
- /* ignore the failure and move on */
- LOG(ERROR) << "VFY: failing opcode 0x" << std::hex
- << (int) dec_insn.opcode_ << " at 0x" << insn_idx << std::dec;
- failure = VERIFY_ERROR_NONE;
-#if 0
/* replace opcode and continue on */
LOG(ERROR) << "VFY: replacing opcode 0x" << std::hex
<< (int) dec_insn.opcode_ << " at 0x" << insn_idx << std::dec;
- if (!ReplaceFailingInstruction(code_item, insn_flags, insn_idx, failure))
- {
+ 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;
@@ -3395,7 +3388,6 @@
/* continue on as if we just handled a throw-verification-error */
failure = VERIFY_ERROR_NONE;
opcode_flag = Instruction::kThrow;
-#endif
}
}
@@ -3595,7 +3587,7 @@
}
bool DexVerifier::ReplaceFailingInstruction(const DexFile::CodeItem* code_item,
- InsnFlags* insn_flags, int insn_idx, VerifyError failure) {
+ 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);
@@ -3672,14 +3664,13 @@
DCHECK(inst->IsThrow());
/* write a NOP over the third code unit, if necessary */
- int width = InsnGetWidth(insn_flags, insn_idx);
+ int width = inst->Size();
switch (width) {
case 2:
/* nothing to do */
break;
case 3:
- // TODO: Add this functionality
- //UpdateCodeUnit(method, insns + 2, Instruction::NOP);
+ UpdateCodeUnit(insns + 2, Instruction::NOP);
break;
default:
/* whoops */
@@ -3688,17 +3679,18 @@
}
/* encode the opcode, with the failure code in the high byte */
- // TODO: REPLACE FAILING OPCODES
- //DCHECK(width == 2 || width == 3);
- //uint16_t new_val = Instruction::THROW_VERIFICATION_ERROR |
- //uint16_t new_val = Instruction::UNUSED_ED |
- //(failure << 8) | (ref_type << (8 + kVerifyErrorRefTypeShift));
- //UpdateCodeUnit(method, insns, new_val);
+ 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;
}
-/* Handle a monitor-enter instruction. */
+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))) {
@@ -3726,7 +3718,6 @@
work_line->monitor_stack_[work_line->monitor_stack_top_++] = insn_idx;
}
-/* Handle a monitor-exit instruction. */
void DexVerifier::HandleMonitorExit(RegisterLine* work_line, uint32_t reg_idx,
uint32_t insn_idx, VerifyError* failure) {
if (!RegTypeIsReference(GetRegisterType(work_line, reg_idx))) {
@@ -3777,23 +3768,17 @@
Method* method = vdata->method_;
const DexFile* dex_file = vdata->dex_file_;
UninitInstanceMap* uninit_map = vdata->uninit_map_.get();
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
- const ClassLoader* class_loader =
- method->GetDeclaringClass()->GetClassLoader();
- Field* field = NULL;
- Class* obj_class;
bool must_be_local = false;
if (!RegTypeIsReference(obj_type)) {
LOG(ERROR) << "VFY: attempt to access field in non-reference type "
<< obj_type;
*failure = VERIFY_ERROR_GENERIC;
- return field;
+ return NULL;
}
- field = class_linker->ResolveField(*dex_file, field_idx, dex_cache,
- class_loader, false);
+ 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 field;
@@ -3807,7 +3792,7 @@
* the <init> method for the object and the field in question is
* declared by this class.
*/
- obj_class = RegTypeReferenceToClass(obj_type, uninit_map);
+ Class* obj_class = RegTypeReferenceToClass(obj_type, uninit_map);
DCHECK(obj_class != NULL);
if (RegTypeIsUninitReference(obj_type)) {
if (!IsInitMethod(method) || method->GetDeclaringClass() != obj_class) {
@@ -3849,21 +3834,14 @@
VerifyError* failure) {
Method* method = vdata->method_;
const DexFile* dex_file = vdata->dex_file_;
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
- const ClassLoader* class_loader =
- method->GetDeclaringClass()->GetClassLoader();
- Field* field;
-
- field = class_linker->ResolveField(*dex_file, field_idx, dex_cache,
- class_loader, true);
+ 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);
- LOG(ERROR) << "VFY: unable to resolve static field";
+ 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;
@@ -4349,7 +4327,7 @@
}
Class* DexVerifier::ResolveClassAndCheckAccess(const DexFile* dex_file,
- uint32_t class_idx, const Class* referrer, VerifyError* failure) {
+ 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);
@@ -4370,6 +4348,103 @@
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->CanAccess(res_method->GetDeclaringClass())) {
+ LOG(ERROR) << "VFY: illegal method access (call "
+ << res_method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+ << "." << res_method->GetName() << " "
+ << res_method->GetSignature() << " 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);
+ return NULL;
+ }
+
+ Class* field_type = ResolveClassAndCheckAccess(dex_file, field_id.type_idx_, referrer, failure);
+ if (field_type == NULL) {
+ DCHECK(*failure != VERIFY_ERROR_NONE);
+ 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->CanAccess(res_field->GetDeclaringClass())) {
+ LOG(ERROR) << "VFY: access denied from "
+ << referrer->GetDescriptor()->ToModifiedUtf8() << " to field "
+ << res_field->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+ << "." << res_field->GetName()->ToModifiedUtf8();
+ *failure = VERIFY_ERROR_ACCESS_FIELD;
+ return NULL;
+ }
+
+ return res_field;
+}
+
DexVerifier::RegType DexVerifier::MergeTypes(RegType type1, RegType type2,
bool* changed) {
RegType result;
@@ -4769,7 +4844,7 @@
DexVerifier::RegType DexVerifier::AdjustForRightShift(
RegisterLine* register_line, int reg, unsigned int shift_count,
- bool is_unsigned_shift, VerifyError* failure) {
+ bool is_unsigned_shift) {
RegType src_type = GetRegisterType(register_line, reg);
RegType new_type;
@@ -4906,10 +4981,6 @@
const DexFile* dex_file = vdata->dex_file_;
const DexFile::CodeItem* code_item = vdata->code_item_;
UninitInstanceMap* uninit_map = vdata->uninit_map_.get();
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
- const ClassLoader* class_loader =
- method->GetDeclaringClass()->GetClassLoader();
Method* res_method;
std::string sig;
@@ -4921,8 +4992,8 @@
* Resolve the method. This could be an abstract or concrete method
* depending on what sort of call we're making.
*/
- res_method = class_linker->ResolveMethod(*dex_file, dec_insn->vB_, dex_cache,
- class_loader, (method_type == METHOD_DIRECT || method_type == METHOD_STATIC));
+ 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_);
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index 0170ebe..8b1f450 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -1147,7 +1147,10 @@
* Returns "true" on success.
*/
static bool ReplaceFailingInstruction(const DexFile::CodeItem* code_item,
- InsnFlags* insn_flags, int insn_idx, VerifyError failure);
+ int insn_idx, VerifyError failure);
+
+ /* Update a 16-bit opcode in a dex file. */
+ static void UpdateCodeUnit(const uint16_t* ptr, uint16_t new_val);
/* Handle a monitor-enter instruction. */
static void HandleMonitorEnter(RegisterLine* work_line, uint32_t reg_idx,
@@ -1401,6 +1404,30 @@
uint32_t class_idx, const Class* referrer, VerifyError* failure);
/*
+ * Resolves a method based on an index and performs access checks to ensure
+ * the referrer can access the resolved method.
+ *
+ * Does not throw exceptions.
+ *
+ * Sets "*failure" on failure.
+ */
+ static Method* ResolveMethodAndCheckAccess(const DexFile* dex_file,
+ uint32_t method_idx, const Class* referrer, VerifyError* failure,
+ bool is_direct);
+
+ /*
+ * Resolves a field based on an index and performs access checks to ensure
+ * the referrer can access the resolved field.
+ *
+ * Exceptions caused by failures are cleared before returning.
+ *
+ * Sets "*failure" on failure.
+ */
+ static Field* ResolveFieldAndCheckAccess(const DexFile* dex_file,
+ uint32_t class_idx, const Class* referrer, VerifyError* failure,
+ bool is_static);
+
+ /*
* Merge two RegType values.
*
* Sets "*changed" to "true" if the result doesn't match "type1".
@@ -1597,7 +1624,7 @@
* Returns the new register type.
*/
static RegType AdjustForRightShift(RegisterLine* register_line, int reg,
- unsigned int shift_count, bool is_unsigned_shift, VerifyError* failure);
+ unsigned int shift_count, bool is_unsigned_shift);
/*
* We're performing an operation like "and-int/2addr" that can be