// Method flags test class generated from the following smali code. The declared-synchronized // flags are there to enforce a 3-byte uLEB128 encoding so we don't have to relayout // the code, but we need to remove them before doing tests. // // .class public LMethodFlags; // .super Ljava/lang/Object; // // .method public static constructor <clinit>()V // .registers 1 // return-void // .end method // // .method public constructor <init>()V // .registers 1 // return-void // .end method // // .method private declared-synchronized foo()V // .registers 1 // return-void // .end method // // .method public declared-synchronized bar()V // .registers 1 // return-void // .end method
// Find the method data for the first method with the given name (from class 0). Note: the pointer // is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index // delta. staticconst uint8_t* FindMethodData(const DexFile* dex_file, constchar* name, /*out*/ uint32_t* method_idx = nullptr) {
ClassAccessor accessor(*dex_file, dex_file->GetClassDef(0));
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
uint32_t method_index = method.GetIndex();
dex::StringIndex name_index = dex_file->GetMethodId(method_index).name_idx_; const dex::StringId& string_id = dex_file->GetStringId(name_index); constchar* str = dex_file->GetStringData(string_id); if (strcmp(name, str) == 0) { if (method_idx != nullptr) {
*method_idx = method_index;
} // Go back 2 lebs to the access flags. const uint8_t* trailing = ReverseSearchUnsignedLeb128(method.GetDataPointer());
trailing = ReverseSearchUnsignedLeb128(trailing); return trailing;
}
}
return nullptr;
}
// Set the method flags to the given value. staticvoid SetMethodFlags(DexFile* dex_file, constchar* method, uint32_t mask) {
uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method));
CHECK(method_flags_ptr != nullptr) << method;
// Unroll this, as we only have three bytes, anyways.
uint8_t base1 = static_cast<uint8_t>(mask & 0x7F);
*(method_flags_ptr++) = (base1 | 0x80);
mask >>= 7;
// Apply the given mask to method flags. staticvoid ApplyMaskToMethodFlags(DexFile* dex_file, constchar* method, uint32_t mask) {
uint32_t value = GetMethodFlags(dex_file, method);
value &= mask;
SetMethodFlags(dex_file, method, value);
}
// Apply the given mask to method flags. staticvoid OrMaskToMethodFlags(DexFile* dex_file, constchar* method, uint32_t mask) {
uint32_t value = GetMethodFlags(dex_file, method);
value |= mask;
SetMethodFlags(dex_file, method, value);
}
// Set code_off to 0 for the method. staticvoid RemoveCode(DexFile* dex_file, constchar* method) { const uint8_t* ptr = FindMethodData(dex_file, method); // Next is flags, pass.
DecodeUnsignedLeb128(&ptr);
// Figure out how many bytes the code_off is. const uint8_t* tmp = ptr;
DecodeUnsignedLeb128(&tmp);
size_t bytes = tmp - ptr;
uint8_t* mod = const_cast<uint8_t*>(ptr); for (size_t i = 1; i < bytes; ++i) {
*(mod++) = 0x80;
}
*mod = 0x00;
}
TEST_F(DexFileVerifierTest, MethodAccessFlagsBase) { // Check that it's OK when the wrong declared-synchronized flag is removed from "foo."
VerifyModification(
kMethodFlagsTestDex, "method_flags_ok",
[](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
},
nullptr);
}
TEST_F(DexFileVerifierTest, MethodAccessFlagsConstructors) { // Make sure we still accept constructors without their flags.
VerifyModification(
kMethodFlagsTestDex, "method_flags_missing_constructor_tag_ok",
[](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
constexpr constchar* kConstructors[] = { "<clinit>", "<init>"}; for (size_t i = 0; i < 2; ++i) { // Constructor with code marked native.
VerifyModification(
kMethodFlagsTestDex, "method_flags_constructor_native",
[&](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative);
}, "has code, but is marked native or abstract"); // Constructor with code marked abstract.
VerifyModification(
kMethodFlagsTestDex, "method_flags_constructor_abstract",
[&](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract);
}, "has code, but is marked native or abstract"); // Constructor as-is without code.
VerifyModification(
kMethodFlagsTestDex, "method_flags_constructor_nocode",
[&](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
RemoveCode(dex_file, kConstructors[i]);
}, "has no code, but is not marked native or abstract"); // Constructor without code marked native.
VerifyModification(
kMethodFlagsTestDex, "method_flags_constructor_native_nocode",
[&](DexFile* dex_file) {
MakeDexVersion37(dex_file);
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative);
RemoveCode(dex_file, kConstructors[i]);
}, "must not be abstract or native"); // Constructor without code marked abstract.
VerifyModification(
kMethodFlagsTestDex, "method_flags_constructor_abstract_nocode",
[&](DexFile* dex_file) {
MakeDexVersion37(dex_file);
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract);
RemoveCode(dex_file, kConstructors[i]);
}, "must not be abstract or native");
} // <init> may only have (modulo ignored): // kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic static constexpr uint32_t kInitAllowed[] = { 0,
kAccPrivate,
kAccProtected,
kAccPublic,
kAccStrict,
kAccVarargs,
kAccSynthetic
}; for (size_t i = 0; i < arraysize(kInitAllowed); ++i) {
VerifyModification(
kMethodFlagsTestDex, "init_allowed_flags",
[&](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
OrMaskToMethodFlags(dex_file, "<init>", kInitAllowed[i]);
},
nullptr);
} // Only one of public-private-protected. for (size_t i = 1; i < 8; ++i) { if (POPCOUNT(i) < 2) { continue;
} // Technically the flags match, but just be defensive here.
uint32_t mask = ((i & 1) != 0 ? kAccPrivate : 0) |
((i & 2) != 0 ? kAccProtected : 0) |
((i & 4) != 0 ? kAccPublic : 0);
VerifyModification(
kMethodFlagsTestDex, "init_one_of_ppp",
[&](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
OrMaskToMethodFlags(dex_file, "<init>", mask);
}, "Method may have only one of public/protected/private");
} // <init> doesn't allow // kAccStatic | kAccFinal | kAccSynchronized | kAccBridge // Need to handle static separately as it has its own error message.
VerifyModification(
kMethodFlagsTestDex, "init_not_allowed_flags",
[&](DexFile* dex_file) {
MakeDexVersion37(dex_file);
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic);
OrMaskToMethodFlags(dex_file, "<init>", kAccStatic);
}, "Constructor 1(LMethodFlags;.<init>) is not flagged correctly wrt/ static"); static constexpr uint32_t kInitNotAllowed[] = {
kAccFinal,
kAccSynchronized,
kAccBridge
}; for (size_t i = 0; i < arraysize(kInitNotAllowed); ++i) {
VerifyModification(
kMethodFlagsTestDex, "init_not_allowed_flags",
[&](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
RemoveCode(dex_file, kMethods[i]);
}, "has no code, but is not marked native or abstract");
// Abstract methods may not have the following flags.
constexpr uint32_t kAbstractDisallowed[] = {
kAccPrivate,
kAccStatic,
kAccFinal,
kAccNative,
kAccStrict,
kAccSynchronized,
}; for (size_t j = 0; j < arraysize(kAbstractDisallowed); ++j) {
VerifyModification(
kMethodFlagsTestDex, "method_flags_abstract_and_disallowed_no_code",
[&](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
RemoveCode(dex_file, kMethods[i]);
// Can't check private and static with foo, as it's in the virtual list and gives a // different error. if (((GetMethodFlags(dex_file, kMethods[i]) & kAccPublic) != 0) &&
((kAbstractDisallowed[j] & (kAccPrivate | kAccStatic)) != 0)) { // Use another breaking flag.
OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAccFinal);
} else {
OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAbstractDisallowed[j]);
}
}, "has disallowed access flags");
}
// Only one of public-private-protected. for (size_t j = 1; j < 8; ++j) { if (POPCOUNT(j) < 2) { continue;
} // Technically the flags match, but just be defensive here.
uint32_t mask = ((j & 1) != 0 ? kAccPrivate : 0) |
((j & 2) != 0 ? kAccProtected : 0) |
((j & 4) != 0 ? kAccPublic : 0);
VerifyModification(
kMethodFlagsTestDex, "method_flags_one_of_ppp",
[&](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, kMethods[i], ~kAccPublic);
OrMaskToMethodFlags(dex_file, kMethods[i], mask);
}, "Method may have only one of public/protected/private");
}
}
}
TEST_F(DexFileVerifierTest, MethodAccessFlagsIgnoredOK) {
constexpr constchar* kMethods[] = { "<clinit>", "<init>", "foo", "bar"}; for (size_t i = 0; i < arraysize(kMethods); ++i) { // All interesting method flags, other flags are to be ignored.
constexpr uint32_t kAllMethodFlags =
kAccPublic |
kAccPrivate |
kAccProtected |
kAccStatic |
kAccFinal |
kAccSynchronized |
kAccBridge |
kAccVarargs |
kAccNative |
kAccAbstract |
kAccStrict |
kAccSynthetic;
constexpr uint32_t kIgnoredMask = ~kAllMethodFlags & 0xFFFF;
VerifyModification(
kMethodFlagsTestDex, "method_flags_ignored",
[&](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
TEST_F(DexFileVerifierTest, B28552165) { // Regression test for bad error string retrieval in different situations. // Using invalid access flags to trigger the error.
VerifyModification(
kMethodFlagsTestDex, "b28552165",
[](DexFile* dex_file) {
OrMaskToMethodFlags(dex_file, "foo", kAccPublic | kAccProtected);
}, "Method may have only one of public/protected/private, LMethodFlags;.foo");
}
// Set of dex files for interface method tests. As it's not as easy to mutate method names, it's // just easier to break up bad cases.
// Standard interface. Use declared-synchronized again for 3B encoding. // // .class public interface LInterfaceMethodFlags; // .super Ljava/lang/Object; // // .method public static constructor <clinit>()V // .registers 1 // return-void // .end method // // .method public abstract declared-synchronized foo()V // .end method staticconstchar kMethodFlagsInterface[] = "ZGV4CjAzNQCOM0odZ5bws1d9GSmumXaK5iE/7XxFpOm8AQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF" "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADwAAAAzAAAAMwA" "AADWAAAA7gAAAAIBAAAFAQAAAQAAAAIAAAADAAAAAwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAABAAA" "AAAAAAABAgAAAQAAAAAAAAD/////AAAAACIBAAAAAAAACDxjbGluaXQ+ABZMSW50ZXJmYWNlTWV0" "aG9kRmxhZ3M7ABJMamF2YS9sYW5nL09iamVjdDsAAVYAA2ZvbwAAAAAAAAABAAAAAAAAAAAAAAAB" "AAAADgAAAAEBAImABJACAYGICAAAAAALAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAMA" "AACEAAAAAwAAAAEAAACQAAAABQAAAAIAAACcAAAABgAAAAEAAACsAAAAAiAAAAUAAADMAAAAAxAA" "AAEAAAAMAQAAASAAAAEAAAAQAQAAACAAAAEAAAAiAQAAABAAAAEAAAA0AQAA";
// To simplify generation of interesting "sub-states" of src_value, allow a "simple" mask to apply // to a src_value, such that mask bit 0 applies to the lowest set bit in src_value, and so on. static uint32_t ApplyMaskShifted(uint32_t src_value, uint32_t mask) {
uint32_t result = 0;
uint32_t mask_index = 0; while (src_value != 0) {
uint32_t index = CTZ(src_value); if (((src_value & (1 << index)) != 0) &&
((mask & (1 << mask_index)) != 0)) {
result |= (1 << index);
}
src_value &= ~(1 << index);
mask_index++;
} return result;
}
OrMaskToMethodFlags(dex_file, "foo", kAccStatic);
}, "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0");
VerifyModification(
kMethodFlagsInterface, "method_flags_interface_private",
[](DexFile* dex_file) {
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic);
OrMaskToMethodFlags(dex_file, "foo", kAccPrivate);
}, "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0");
// Find the method data for the first method with the given name (from class 0). Note: the pointer // is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index // delta. staticconst uint8_t* FindFieldData(const DexFile* dex_file, constchar* name) {
ClassAccessor accessor(*dex_file, dex_file->GetClassDef(0));
for (const ClassAccessor::Field& field : accessor.GetFields()) {
uint32_t field_index = field.GetIndex();
dex::StringIndex name_index = dex_file->GetFieldId(field_index).name_idx_; const dex::StringId& string_id = dex_file->GetStringId(name_index); constchar* str = dex_file->GetStringData(string_id); if (strcmp(name, str) == 0) { // Go to the back of the access flags. return ReverseSearchUnsignedLeb128(field.GetDataPointer());
}
}
return nullptr;
}
// Set the method flags to the given value. staticvoid SetFieldFlags(DexFile* dex_file, constchar* field, uint32_t mask) {
uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field));
CHECK(field_flags_ptr != nullptr) << field;
// Unroll this, as we only have three bytes, anyways.
uint8_t base1 = static_cast<uint8_t>(mask & 0x7F);
*(field_flags_ptr++) = (base1 | 0x80);
mask >>= 7;
// Apply the given mask to method flags. staticvoid ApplyMaskToFieldFlags(DexFile* dex_file, constchar* field, uint32_t mask) {
uint32_t value = GetFieldFlags(dex_file, field);
value &= mask;
SetFieldFlags(dex_file, field, value);
}
// Apply the given mask to method flags. staticvoid OrMaskToFieldFlags(DexFile* dex_file, constchar* field, uint32_t mask) {
uint32_t value = GetFieldFlags(dex_file, field);
value |= mask;
SetFieldFlags(dex_file, field, value);
}
// Standard class. Use declared-synchronized again for 3B encoding. // // .class public LFieldFlags; // .super Ljava/lang/Object; // // .field declared-synchronized public foo:I // // .field declared-synchronized public static bar:I
TEST_F(DexFileVerifierTest, FieldAccessFlagsBase) { // Check that it's OK when the wrong declared-synchronized flag is removed from "foo."
VerifyModification(
kFieldFlagsTestDex, "field_flags_ok",
[](DexFile* dex_file) {
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
},
nullptr);
}
TEST_F(DexFileVerifierTest, FieldAccessFlagsWrongList) { // Mark the field so that it should appear in the opposite list (instance vs static).
VerifyModification(
kFieldFlagsTestDex, "field_flags_wrong_list",
[](DexFile* dex_file) {
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
OrMaskToFieldFlags(dex_file, "foo", kAccStatic);
}, "Static/instance field not in expected list");
VerifyModification(
kFieldFlagsTestDex, "field_flags_wrong_list",
[](DexFile* dex_file) {
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
ApplyMaskToFieldFlags(dex_file, "bar", ~kAccStatic);
}, "Static/instance field not in expected list");
}
TEST_F(DexFileVerifierTest, FieldAccessFlagsPPP) { staticconstchar* kFields[] = { "foo", "bar" }; for (size_t i = 0; i < arraysize(kFields); ++i) { // Should be OK to remove public.
VerifyModification(
kFieldFlagsTestDex, "field_flags_non_public",
[&](DexFile* dex_file) {
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic);
uint32_t mask = ApplyMaskShifted(kAccFlags, j);
OrMaskToFieldFlags(dex_file, kFields[i], mask);
}, "Field may have only one of public/protected/private");
}
}
}
TEST_F(DexFileVerifierTest, FieldAccessFlagsIgnoredOK) {
constexpr constchar* kFields[] = { "foo", "bar"}; for (size_t i = 0; i < arraysize(kFields); ++i) { // All interesting method flags, other flags are to be ignored.
constexpr uint32_t kAllFieldFlags =
kAccPublic |
kAccPrivate |
kAccProtected |
kAccStatic |
kAccFinal |
kAccVolatile |
kAccTransient |
kAccSynthetic |
kAccEnum;
constexpr uint32_t kIgnoredMask = ~kAllFieldFlags & 0xFFFF;
VerifyModification(
kFieldFlagsTestDex, "field_flags_ignored",
[&](DexFile* dex_file) {
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized);
OrMaskToFieldFlags(dex_file, kFields[i], kAccVolatile | kAccFinal);
}, "Fields may not be volatile and final");
}
}
// Standard interface. Needs to be separate from class as interfaces do not allow instance fields. // Use declared-synchronized again for 3B encoding. // // .class public interface LInterfaceFieldFlags; // .super Ljava/lang/Object; // // .field declared-synchronized public static final foo:I
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
OrMaskToFieldFlags(dex_file, "foo", kAccProtected);
},
nullptr); // Should be allowed in older dex versions for backwards compatibility.
VerifyModification(
kFieldFlagsInterfaceTestDex, "field_flags_interface_protected",
[](DexFile* dex_file) {
MakeDexVersion37(dex_file);
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
OrMaskToFieldFlags(dex_file, "foo", kAccProtected);
}, "Interface field is not public final static");
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
OrMaskToFieldFlags(dex_file, "foo", kAccPrivate);
},
nullptr); // Should be allowed in older dex versions for backwards compatibility.
VerifyModification(
kFieldFlagsInterfaceTestDex, "field_flags_interface_private",
[](DexFile* dex_file) {
MakeDexVersion37(dex_file);
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
OrMaskToFieldFlags(dex_file, "foo", kAccPrivate);
}, "Interface field is not public final static");
uint32_t bits = POPCOUNT(kInterfaceDisallowed); for (uint32_t i = 1; i < (1u << bits); ++i) {
VerifyModification(
kFieldFlagsInterfaceTestDex, "field_flags_interface_disallowed",
[&](DexFile* dex_file) {
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i); if ((mask & kAccProtected) != 0) {
mask &= ~kAccProtected;
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
}
OrMaskToFieldFlags(dex_file, "foo", mask);
},
nullptr); // Should be allowed in older dex versions for backwards compatibility.
VerifyModification(
kFieldFlagsInterfaceTestDex, "field_flags_interface_disallowed",
[&](DexFile* dex_file) {
MakeDexVersion37(dex_file);
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i); if ((mask & kAccProtected) != 0) {
mask &= ~kAccProtected;
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic);
}
OrMaskToFieldFlags(dex_file, "foo", mask);
}, "Interface field has disallowed flag");
}
}
// Standard bad interface. Needs to be separate from class as interfaces do not allow instance // fields. Use declared-synchronized again for 3B encoding. // // .class public interface LInterfaceFieldFlags; // .super Ljava/lang/Object; // // .field declared-synchronized public final foo:I
TEST_F(DexFileVerifierTest, FieldAccessFlagsInterfaceNonStatic) {
VerifyModification(
kFieldFlagsInterfaceBadTestDex, "field_flags_interface_non_static",
[](DexFile* dex_file) {
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
},
nullptr); // Should be allowed in older dex versions for backwards compatibility.
VerifyModification(
kFieldFlagsInterfaceBadTestDex, "field_flags_interface_non_static",
[](DexFile* dex_file) {
MakeDexVersion37(dex_file);
ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized);
}, "Interface field is not public final static");
}
TEST_F(DexFileVerifierTest, SectionAlignment) {
{ // The input dex file should be good before modification. Any file is fine, as long as it // uses all sections.
std::string error_msg;
std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kGoodTestDex,
kLocationString,
&error_msg));
ASSERT_TRUE(raw.get() != nullptr) << error_msg;
}
static_assert(kSections == 7, "kSections is wrong"); default:
LOG(FATAL) << "Unexpected section";
UNREACHABLE();
}
ASSERT_TRUE(off_ptr != nullptr);
ASSERT_NE(*off_ptr, 0U) << i; // Should already contain a value (in use).
(*off_ptr)++; // Add one, which should misalign it (all the sections // above are aligned by 4).
}, "should be aligned by 4 for");
}
}
TEST_F(DexFileVerifierTest, ProtoOrdering) {
{ // The input dex file should be good before modification.
std::string error_msg;
std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kProtoOrderingTestDex,
kLocationString,
&error_msg));
ASSERT_TRUE(raw.get() != nullptr) << error_msg;
}
// Modify the order of the ProtoIds for two overloads of "foo" with the // same return type and one having longer parameter list than the other. for (size_t i = 0; i != 2; ++i) {
VerifyModification(
kProtoOrderingTestDex, "proto_ordering",
[i](DexFile* dex_file) {
uint32_t method_idx; const uint8_t* data = FindMethodData(dex_file, "foo", &method_idx);
CHECK(data != nullptr); // There should be 2 methods called "foo".
CHECK_LT(method_idx + 1u, dex_file->NumMethodIds());
CHECK_EQ(dex_file->GetMethodId(method_idx).name_idx_,
dex_file->GetMethodId(method_idx + 1).name_idx_);
CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_.index_ + 1u,
dex_file->GetMethodId(method_idx + 1).proto_idx_.index_); // Their return types should be the same.
dex::ProtoIndex proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_; const dex::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx);
dex::ProtoIndex proto2_idx(proto1_idx.index_ + 1u); const dex::ProtoId& proto2 = dex_file->GetProtoId(proto2_idx);
CHECK_EQ(proto1.return_type_idx_, proto2.return_type_idx_); // And the first should not have any parameters while the second should have some.
CHECK(!DexFileParameterIterator(*dex_file, proto1).HasNext());
CHECK(DexFileParameterIterator(*dex_file, proto2).HasNext()); if (i == 0) { // Swap the proto parameters and shorties to break the ordering.
std::swap(const_cast<uint32_t&>(proto1.parameters_off_), const_cast<uint32_t&>(proto2.parameters_off_));
std::swap(const_cast<dex::StringIndex&>(proto1.shorty_idx_), const_cast<dex::StringIndex&>(proto2.shorty_idx_));
} else { // Copy the proto parameters and shorty to create duplicate proto id. const_cast<uint32_t&>(proto1.parameters_off_) = proto2.parameters_off_; const_cast<dex::StringIndex&>(proto1.shorty_idx_) = proto2.shorty_idx_;
}
}, "Out-of-order proto_id arguments");
}
}
// To generate a base64 encoded Dex file version 037 from Smali files, use: // // smali assemble --api 24 -o classes.dex class1.smali [class2.smali ...] // base64 classes.dex >classes.dex.base64
// Dex file version 037 generated from: // // .class public LB28685551; // .super LB28685551;
TEST_F(DexFileVerifierTest, ClassExtendsItself) {
VerifyModification(
kClassExtendsItselfTestDex, "class_extends_itself",
[]([[maybe_unused]] DexFile* dex_file) { /* empty */ }, "Class with same type idx as its superclass: '0'");
}
// Dex file version 037 generated from: // // .class public LFoo; // .super LBar; // // and: // // .class public LBar; // .super LFoo;
TEST_F(DexFileVerifierTest, ClassesExtendOneAnother) {
VerifyModification(
kClassesExtendOneAnotherTestDex, "classes_extend_one_another",
[]([[maybe_unused]] DexFile* dex_file) { /* empty */ }, "Invalid class definition ordering: class with type idx: '1' defined before" " superclass with type idx: '0'");
}
// Dex file version 037 generated from: // // .class public LAll; // .super LYour; // // and: // // .class public LYour; // .super LBase; // // and: // // .class public LBase; // .super LAll;
TEST_F(DexFileVerifierTest, CircularClassInheritance) {
VerifyModification(
kCircularClassInheritanceTestDex, "circular_class_inheritance",
[]([[maybe_unused]] DexFile* dex_file) { /* empty */ }, "Invalid class definition ordering: class with type idx: '1' defined before" " superclass with type idx: '0'");
}
// Dex file version 037 generated from: // // .class public abstract interface LInterfaceImplementsItself; // .super Ljava/lang/Object; // .implements LInterfaceImplementsItself;
TEST_F(DexFileVerifierTest, InterfacesImplementOneAnother) {
VerifyModification(
kInterfacesImplementOneAnotherTestDex, "interfaces_implement_one_another",
[]([[maybe_unused]] DexFile* dex_file) { /* empty */ }, "Invalid class definition ordering: class with type idx: '1' defined before" " implemented interface with type idx: '0'");
}
TEST_F(DexFileVerifierTest, CircularInterfaceImplementation) {
VerifyModification(
kCircularInterfaceImplementationTestDex, "circular_interface_implementation",
[]([[maybe_unused]] DexFile* dex_file) { /* empty */ }, "Invalid class definition ordering: class with type idx: '2' defined before" " implemented interface with type idx: '0'");
}
TEST_F(DexFileVerifierTest, Checksum) {
size_t length;
std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kGoodTestDex, &length));
CHECK(dex_bytes != nullptr); // Note: `dex_file` will be destroyed before `dex_bytes`.
std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
std::string error_msg;
// Good checksum: all pass.
EXPECT_TRUE(dex::Verify(dex_file.get(), "good checksum, no verify", /*verify_checksum=*/false,
&error_msg));
EXPECT_TRUE(dex::Verify(dex_file.get(), "good checksum, verify", /*verify_checksum=*/true,
&error_msg));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.