summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2025-02-13 14:54:16 +0000
committer VladimĂ­r Marko <vmarko@google.com> 2025-02-14 07:39:37 -0800
commit8c80783c05b2767140f9229b523c2ce601f31ddf (patch)
tree722bebe84fa8581421f5e9f44398acab4a4b3453
parentce10dc6fbdeaef31f0e7ff6e80e0678cb3e13e94 (diff)
verifier: Reject `filled-new-array/-range` with `[J`/`[D`.
We were not enforcing the specified requirement from https://source.android.com/docs/core/runtime/dalvik-bytecode for `filled-new-array/-range`. We now reject the descriptors `[J` and `[D` in the verifier and defer arrays of primitive types other than `[I` to the interpreter. All these cases would have previously hit a `DCHECK()` in the compiler. Test: m test-art-host-gtest Test: testrunner.py --host --optimizing Change-Id: Iaf91465faa8ed9599abe2504796a2d9bc5bd4678
-rw-r--r--compiler/optimizing/instruction_builder.cc4
-rw-r--r--libdexfile/dex/dex_instruction.h4
-rw-r--r--libdexfile/dex/dex_instruction_list.h4
-rw-r--r--runtime/interpreter/interpreter_common.cc16
-rw-r--r--runtime/verifier/method_verifier.cc210
-rw-r--r--runtime/verifier/verifier_enums.h6
-rw-r--r--test/412-new-array/smali/FillArrayData.smali (renamed from test/412-new-array/smali/fill_array_data.smali)16
-rw-r--r--test/412-new-array/smali/FilledNewArray.smali (renamed from test/412-new-array/smali/filled_new_array.smali)16
-rw-r--r--test/412-new-array/smali/FilledNewArrayInternalErrorB.smali26
-rw-r--r--test/412-new-array/smali/FilledNewArrayInternalErrorC.smali26
-rw-r--r--test/412-new-array/smali/FilledNewArrayInternalErrorF.smali26
-rw-r--r--test/412-new-array/smali/FilledNewArrayInternalErrorS.smali26
-rw-r--r--test/412-new-array/smali/FilledNewArrayInternalErrorZ.smali26
-rw-r--r--test/412-new-array/smali/FilledNewArrayVerifyError.smali26
-rw-r--r--test/412-new-array/smali/FilledNewArrayVerifyErrorD.smali26
-rw-r--r--test/412-new-array/smali/FilledNewArrayVerifyErrorJ.smali26
-rw-r--r--test/412-new-array/smali/filled_new_array_verify_error.smali10
-rw-r--r--test/412-new-array/src/Main.java35
18 files changed, 416 insertions, 113 deletions
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index c8086cff99..f65586f505 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -2552,9 +2552,7 @@ HNewArray* HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc,
const char* descriptor = dex_file_->GetTypeDescriptor(type_index);
DCHECK_EQ(descriptor[0], '[') << descriptor;
char primitive = descriptor[1];
- DCHECK(primitive == 'I'
- || primitive == 'L'
- || primitive == '[') << descriptor;
+ DCHECK(primitive == 'I' || primitive == 'L' || primitive == '[') << descriptor;
bool is_reference_array = (primitive == 'L') || (primitive == '[');
DataType::Type type = is_reference_array ? DataType::Type::kReference : DataType::Type::kInt32;
diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h
index 07f035c108..e47e3ebf87 100644
--- a/libdexfile/dex/dex_instruction.h
+++ b/libdexfile/dex/dex_instruction.h
@@ -197,6 +197,7 @@ class Instruction {
kVerifyRegBCallSite = 0x0800000,
kVerifyRegBMethodHandle = 0x1000000,
kVerifyRegBPrototype = 0x2000000,
+ kVerifyRegBFilledNewArray = 0x4000000,
};
// Collect the enums in a struct for better locality.
@@ -606,7 +607,8 @@ class Instruction {
kVerifyRegBNewInstance |
kVerifyRegBString |
kVerifyRegBType |
- kVerifyRegBWide;
+ kVerifyRegBWide |
+ kVerifyRegBFilledNewArray;
return VerifyFlagsOf(opcode) & kMask;
}
diff --git a/libdexfile/dex/dex_instruction_list.h b/libdexfile/dex/dex_instruction_list.h
index 0fd2a6b5bb..bbd7abd8e5 100644
--- a/libdexfile/dex/dex_instruction_list.h
+++ b/libdexfile/dex/dex_instruction_list.h
@@ -55,8 +55,8 @@
V(0x21, ARRAY_LENGTH, "array-length", k12x, kIndexNone, kContinue | kThrow, 0, kVerifyRegA | kVerifyRegB) \
V(0x22, NEW_INSTANCE, "new-instance", k21c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyRegBNewInstance) \
V(0x23, NEW_ARRAY, "new-array", k22c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyRegB | kVerifyRegCNewArray) \
- V(0x24, FILLED_NEW_ARRAY, "filled-new-array", k35c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBType | kVerifyVarArg) \
- V(0x25, FILLED_NEW_ARRAY_RANGE, "filled-new-array/range", k3rc, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBType | kVerifyVarArgRange) \
+ V(0x24, FILLED_NEW_ARRAY, "filled-new-array", k35c, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBFilledNewArray | kVerifyVarArg) \
+ V(0x25, FILLED_NEW_ARRAY_RANGE, "filled-new-array/range", k3rc, kIndexTypeRef, kContinue | kThrow, kClobber, kVerifyRegBFilledNewArray | kVerifyVarArgRange) \
V(0x26, FILL_ARRAY_DATA, "fill-array-data", k31t, kIndexNone, kContinue | kThrow, kClobber, kVerifyRegA | kVerifyArrayData) \
V(0x27, THROW, "throw", k11x, kIndexNone, kThrow, 0, kVerifyRegA) \
V(0x28, GOTO, "goto", k10t, kIndexNone, kBranch | kUnconditional, 0, kVerifyBranchTarget) \
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 5024b16ba2..5deb0e1881 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -1419,14 +1419,14 @@ bool DoFilledNewArray(const Instruction* inst,
ObjPtr<mirror::Class> component_class = array_class->GetComponentType();
const bool is_primitive_int_component = component_class->IsPrimitiveInt();
if (UNLIKELY(component_class->IsPrimitive() && !is_primitive_int_component)) {
- if (component_class->IsPrimitiveLong() || component_class->IsPrimitiveDouble()) {
- ThrowRuntimeException("Bad filled array request for type %s",
- component_class->PrettyDescriptor().c_str());
- } else {
- self->ThrowNewExceptionF("Ljava/lang/InternalError;",
- "Found type %s; filled-new-array not implemented for anything but 'int'",
- component_class->PrettyDescriptor().c_str());
- }
+ // Verifier rejects `filled-new-array/-range` with descriptors `[J` and `[D`.
+ // These are forbidden, see https://source.android.com/docs/core/runtime/dalvik-bytecode .
+ DCHECK(!component_class->IsPrimitiveLong());
+ DCHECK(!component_class->IsPrimitiveDouble());
+ self->ThrowNewExceptionF(
+ "Ljava/lang/InternalError;",
+ "Found type %s; filled-new-array not implemented for anything but 'int'",
+ component_class->PrettyDescriptor().c_str());
return false;
}
ObjPtr<mirror::Object> new_array = mirror::Array::Alloc(
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index d0827ba0b2..fce0b2b374 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -422,25 +422,18 @@ class MethodVerifierImpl : public ::art::verifier::MethodVerifier {
// Perform static checks on an instruction referencing a constant method handle. All we do here
// is ensure that the method index is in the valid range.
bool CheckMethodHandleIndex(uint32_t idx) {
- uint32_t limit = dex_file_->NumMethodHandles();
- if (UNLIKELY(idx >= limit)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad method handle index " << idx << " (max "
- << limit << ")";
+ if (UNLIKELY(idx >= dex_file_->NumMethodHandles())) {
+ FailBadMethodHandleIndex(idx);
return false;
}
return true;
}
- // Perform static checks on a "new-instance" instruction. Specifically, make sure the class
- // reference isn't for an array class.
- bool CheckNewInstance(dex::TypeIndex idx);
-
// Perform static checks on a prototype indexing instruction. All we do here is ensure that the
// prototype index is in the valid range.
bool CheckPrototypeIndex(uint32_t idx) {
- if (UNLIKELY(idx >= dex_file_->GetHeader().proto_ids_size_)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad prototype index " << idx << " (max "
- << dex_file_->GetHeader().proto_ids_size_ << ")";
+ if (UNLIKELY(idx >= dex_file_->NumProtoIds())) {
+ FailBadPrototypeIndex(idx);
return false;
}
return true;
@@ -448,9 +441,8 @@ class MethodVerifierImpl : public ::art::verifier::MethodVerifier {
/* Ensure that the string index is in the valid range. */
bool CheckStringIndex(uint32_t idx) {
- if (UNLIKELY(idx >= dex_file_->GetHeader().string_ids_size_)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad string index " << idx << " (max "
- << dex_file_->GetHeader().string_ids_size_ << ")";
+ if (UNLIKELY(idx >= dex_file_->NumStringIds())) {
+ FailBadStringIndex(idx);
return false;
}
return true;
@@ -460,15 +452,22 @@ class MethodVerifierImpl : public ::art::verifier::MethodVerifier {
// index is in the valid range.
bool CheckTypeIndex(dex::TypeIndex idx) {
if (UNLIKELY(idx.index_ >= dex_file_->GetHeader().type_ids_size_)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx.index_ << " (max "
- << dex_file_->GetHeader().type_ids_size_ << ")";
+ FailBadTypeIndex(idx);
return false;
}
return true;
}
- // Perform static checks on a "new-array" instruction. Specifically, make sure they aren't
- // creating an array of arrays that causes the number of dimensions to exceed 255.
+ // Perform static checks on a `new-instance` instruction. Specifically, make sure the class
+ // reference isn't for an array class.
+ bool CheckNewInstance(dex::TypeIndex idx);
+
+ // Perform static checks on a `*new-array*` instruction. Specifically, make sure it
+ // references an array class with the number of dimensions not exceeding 255.
+ // For `filled-new-array*`, check for a valid component type; `I` is accepted, `J` and `D`
+ // are rejected in line with the specification and other primitive component types are marked
+ // for interpreting (throws `InternalError` in interpreter and the compiler cannot handle them).
+ template <bool kFilled>
bool CheckNewArray(dex::TypeIndex idx);
// Determine if the relative `offset` targets a valid dex pc.
@@ -565,8 +564,8 @@ class MethodVerifierImpl : public ::art::verifier::MethodVerifier {
// This has the side-effect of validating the signature.
bool SetTypesFromSignature() REQUIRES_SHARED(Locks::mutator_lock_);
- // Perform verification of a new array instruction
- void VerifyNewArray(const Instruction* inst, bool is_filled, bool is_range)
+ // Perform verification of a `filled-new-array/-range` instruction.
+ bool VerifyFilledNewArray(const Instruction* inst, bool is_range)
REQUIRES_SHARED(Locks::mutator_lock_);
// Helper to perform verification on puts of primitive type.
@@ -731,6 +730,41 @@ class MethodVerifierImpl : public ::art::verifier::MethodVerifier {
<< "bad method index " << method_idx << " (max " << dex_file_->NumMethodIds() << ")";
}
+ NO_INLINE void FailBadMethodHandleIndex(uint32_t idx) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "bad method handle index " << idx << " (max " << dex_file_->NumMethodHandles() << ")";
+ }
+
+ NO_INLINE void FailBadPrototypeIndex(uint32_t idx) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "bad prototype index " << idx << " (max " << dex_file_->NumProtoIds() << ")";
+ }
+
+ NO_INLINE void FailBadStringIndex(uint32_t idx) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "bad string index " << idx << " (max " << dex_file_->NumStringIds() << ")";
+ }
+
+ NO_INLINE void FailBadTypeIndex(dex::TypeIndex idx) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "bad type index " << idx.index_ << " (max " << dex_file_->NumTypeIds() << ")";
+ }
+
+ NO_INLINE void FailBadNewArrayNotArray(const char* descriptor) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "can't new-array class '" << descriptor << "' (not an array)";
+ }
+
+ NO_INLINE void FailBadNewArrayTooManyDimensions(const char* descriptor) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "can't new-array class '" << descriptor << "' (exceeds limit)";
+ }
+
+ NO_INLINE void FailBadFilledNewArray(const char* descriptor) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD)
+ << "can't fill-new-array class '" << descriptor << "' (wide component type)";
+ }
+
NO_INLINE void FailBranchOffsetZero(uint32_t dex_pc) {
work_insn_idx_ = dex_pc; // Let `Fail()` record the dex PC of the failing instruction.
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "branch offset of zero not allowed.";
@@ -960,7 +994,7 @@ class MethodVerifierImpl : public ::art::verifier::MethodVerifier {
fail_type = VERIFY_ERROR_BAD_CLASS_HARD;
}
FailForRegisterType(vsrc, check_type, src_type, fail_type);
- return false;
+ return fail_type != VERIFY_ERROR_BAD_CLASS_HARD;
}
if (check_type.IsLowHalf()) {
const RegType& src_type_h = work_line_->GetRegisterType(this, vsrc + 1);
@@ -1960,6 +1994,10 @@ inline bool MethodVerifierImpl::VerifyInstruction(uint32_t dex_pc,
case Instruction::kVerifyRegBPrototype:
result = result && CheckPrototypeIndex(inst->VRegB(kFormat, inst_data));
break;
+ case Instruction::kVerifyRegBFilledNewArray:
+ result = result &&
+ CheckNewArray</*kFilled=*/ true>(dex::TypeIndex(inst->VRegB(kFormat, inst_data)));
+ break;
case Instruction::kVerifyNothing:
break;
}
@@ -1973,7 +2011,7 @@ inline bool MethodVerifierImpl::VerifyInstruction(uint32_t dex_pc,
result = result && CheckFieldIndex(inst, inst_data, inst->VRegC(kFormat));
break;
case Instruction::kVerifyRegCNewArray:
- result = result && CheckNewArray(dex::TypeIndex(inst->VRegC(kFormat)));
+ result = result && CheckNewArray</*kFilled=*/ false>(dex::TypeIndex(inst->VRegC(kFormat)));
break;
case Instruction::kVerifyRegCType:
result = result && CheckTypeIndex(dex::TypeIndex(inst->VRegC(kFormat)));
@@ -2041,9 +2079,7 @@ inline bool MethodVerifierImpl::VerifyInstruction(uint32_t dex_pc,
}
inline bool MethodVerifierImpl::CheckNewInstance(dex::TypeIndex idx) {
- if (UNLIKELY(idx.index_ >= dex_file_->GetHeader().type_ids_size_)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx.index_ << " (max "
- << dex_file_->GetHeader().type_ids_size_ << ")";
+ if (!CheckTypeIndex(idx)) {
return false;
}
// We don't need the actual class, just a pointer to the class name.
@@ -2052,36 +2088,42 @@ inline bool MethodVerifierImpl::CheckNewInstance(dex::TypeIndex idx) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "can't call new-instance on type '" << descriptor << "'";
return false;
} else if (UNLIKELY(descriptor == "Ljava/lang/Class;")) {
- // An unlikely new instance on Class is not allowed. Fall back to interpreter to ensure an
- // exception is thrown when this statement is executed (compiled code would not do that).
+ // An unlikely new instance on Class is not allowed.
Fail(VERIFY_ERROR_INSTANTIATION);
}
return true;
}
-bool MethodVerifierImpl::CheckNewArray(dex::TypeIndex idx) {
- if (UNLIKELY(idx.index_ >= dex_file_->GetHeader().type_ids_size_)) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "bad type index " << idx.index_ << " (max "
- << dex_file_->GetHeader().type_ids_size_ << ")";
+template <bool kFilled>
+inline bool MethodVerifierImpl::CheckNewArray(dex::TypeIndex idx) {
+ if (!CheckTypeIndex(idx)) {
return false;
}
- int bracket_count = 0;
const char* descriptor = dex_file_->GetTypeDescriptor(idx);
const char* cp = descriptor;
- while (*cp++ == '[') {
- bracket_count++;
+ while (*cp == '[') {
+ ++cp;
}
- if (UNLIKELY(bracket_count == 0)) {
+ size_t bracket_count = static_cast<size_t>(cp - descriptor);
+ if (UNLIKELY(bracket_count == 0u)) {
/* The given class must be an array type. */
- Fail(VERIFY_ERROR_BAD_CLASS_HARD)
- << "can't new-array class '" << descriptor << "' (not an array)";
+ FailBadNewArrayNotArray(descriptor);
return false;
- } else if (UNLIKELY(bracket_count > 255)) {
+ } else if (UNLIKELY(bracket_count > 255u)) {
/* It is illegal to create an array of more than 255 dimensions. */
- Fail(VERIFY_ERROR_BAD_CLASS_HARD)
- << "can't new-array class '" << descriptor << "' (exceeds limit)";
+ FailBadNewArrayTooManyDimensions(descriptor);
return false;
}
+ if (kFilled && bracket_count == 1u && UNLIKELY(*cp != 'I' && *cp != 'L')) {
+ if (UNLIKELY(*cp == 'J') || UNLIKELY(*cp == 'D')) {
+ // Forbidden, see https://source.android.com/docs/core/runtime/dalvik-bytecode .
+ FailBadFilledNewArray(descriptor);
+ return false;
+ } else {
+ // Fall back to interpreter to throw `InternalError`. Compiler does not handle this case.
+ Fail(VERIFY_ERROR_FILLED_NEW_ARRAY);
+ }
+ }
return true;
}
@@ -3158,15 +3200,30 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g
work_line_->SetRegisterTypeForNewInstance(vA, uninit_type, work_insn_idx_);
break;
}
- case Instruction::NEW_ARRAY:
- VerifyNewArray(inst, false, false);
+ case Instruction::NEW_ARRAY: {
+ // Make sure the "size" register has a valid type.
+ if (!VerifyRegisterType(inst->VRegB_22c(), RegType::Kind::kInteger)) {
+ return false;
+ }
+ // Dex file verifier ensures that all valid type indexes reference valid descriptors and the
+ // `CheckNewArray()` ensures that the descriptor starts with an `[` before we get to the
+ // code flow verification. So, we should see only array types here.
+ const RegType& res_type = ResolveClass<CheckAccess::kYes>(dex::TypeIndex(inst->VRegC_22c()));
+ DCHECK(res_type.IsArrayTypes());
+ // Set the register type to the array class.
+ work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_22c(), res_type);
break;
+ }
case Instruction::FILLED_NEW_ARRAY:
- VerifyNewArray(inst, true, false);
+ if (!VerifyFilledNewArray(inst, /*is_range=*/ false)) {
+ return false;
+ }
just_set_result = true; // Filled new array sets result register
break;
case Instruction::FILLED_NEW_ARRAY_RANGE:
- VerifyNewArray(inst, true, true);
+ if (!VerifyFilledNewArray(inst, /*is_range=*/ true)) {
+ return false;
+ }
just_set_result = true; // Filled new array range sets result register
break;
case Instruction::CMPL_FLOAT:
@@ -4929,54 +4986,41 @@ bool MethodVerifierImpl::CheckSignaturePolymorphicReceiver(const Instruction* in
return true;
}
-void MethodVerifierImpl::VerifyNewArray(const Instruction* inst,
- bool is_filled,
- bool is_range) {
+bool MethodVerifierImpl::VerifyFilledNewArray(const Instruction* inst, bool is_range) {
dex::TypeIndex type_idx;
- if (!is_filled) {
- DCHECK_EQ(inst->Opcode(), Instruction::NEW_ARRAY);
- type_idx = dex::TypeIndex(inst->VRegC_22c());
- } else if (!is_range) {
+ if (!is_range) {
DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY);
type_idx = dex::TypeIndex(inst->VRegB_35c());
} else {
DCHECK_EQ(inst->Opcode(), Instruction::FILLED_NEW_ARRAY_RANGE);
type_idx = dex::TypeIndex(inst->VRegB_3rc());
}
+ // Dex file verifier ensures that all valid type indexes reference valid descriptors and the
+ // `CheckNewArray()` ensures that the descriptor starts with an `[` before we get to the
+ // code flow verification. So, we should see only array types here.
const RegType& res_type = ResolveClass<CheckAccess::kYes>(type_idx);
- if (res_type.IsConflict()) { // bad class
- DCHECK_NE(failures_.size(), 0U);
- } else {
- // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
- if (!res_type.IsArrayTypes()) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "new-array on non-array class " << res_type;
- } else if (!is_filled) {
- /* make sure "size" register is valid type */
- VerifyRegisterType(inst->VRegB_22c(), RegType::Kind::kInteger);
- /* set register type to array class */
- work_line_->SetRegisterType<LockOp::kClear>(inst->VRegA_22c(), res_type);
- } else {
- DCHECK(!res_type.IsUnresolvedMergedReference());
- // 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.
- const RegType& expected_type = reg_types_.GetComponentType(res_type);
- uint32_t arg_count = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c();
- uint32_t arg[5];
- if (!is_range) {
- inst->GetVarArgs(arg);
- }
- for (size_t ui = 0; ui < arg_count; ui++) {
- uint32_t get_reg = is_range ? inst->VRegC_3rc() + ui : arg[ui];
- VerifyRegisterType(get_reg, expected_type);
- if (flags_.have_pending_hard_failure_) {
- // Don't continue on hard failures.
- return;
- }
- }
- // filled-array result goes into "result" register
- work_line_->SetResultRegisterType(res_type);
+ DCHECK(res_type.IsArrayTypes());
+ // TODO: check Compiler::CanAccessTypeWithoutChecks returns false when res_type is unresolved
+ DCHECK(!res_type.IsUnresolvedMergedReference());
+ // Verify each input register. It's legal, if silly, for arg_count to be zero.
+ const RegType& expected_type = reg_types_.GetComponentType(res_type);
+ uint32_t arg_count = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c();
+ uint32_t arg[5];
+ if (!is_range) {
+ inst->GetVarArgs(arg);
+ }
+ for (size_t ui = 0; ui < arg_count; ui++) {
+ uint32_t get_reg = is_range ? inst->VRegC_3rc() + ui : arg[ui];
+ if (!VerifyRegisterType(get_reg, expected_type)) {
+ // Don't continue on hard failures.
+ DCHECK(flags_.have_pending_hard_failure_);
+ return false;
}
+ DCHECK(!flags_.have_pending_hard_failure_);
}
+ // filled-array result goes into "result" register
+ work_line_->SetResultRegisterType(res_type);
+ return true;
}
void MethodVerifierImpl::VerifyAGet(const Instruction* inst,
@@ -5552,6 +5596,7 @@ static inline bool CanRuntimeHandleVerificationFailure(uint32_t encountered_fail
verifier::VerifyError::VERIFY_ERROR_NO_CLASS |
verifier::VerifyError::VERIFY_ERROR_CLASS_CHANGE |
verifier::VerifyError::VERIFY_ERROR_INSTANTIATION |
+ verifier::VerifyError::VERIFY_ERROR_FILLED_NEW_ARRAY |
verifier::VerifyError::VERIFY_ERROR_ACCESS_CLASS |
verifier::VerifyError::VERIFY_ERROR_ACCESS_FIELD |
verifier::VerifyError::VERIFY_ERROR_NO_METHOD |
@@ -5826,6 +5871,7 @@ std::ostream& MethodVerifier::Fail(VerifyError error, bool pending_exc) {
case VERIFY_ERROR_ACCESS_FIELD:
case VERIFY_ERROR_ACCESS_METHOD:
case VERIFY_ERROR_INSTANTIATION:
+ case VERIFY_ERROR_FILLED_NEW_ARRAY:
case VERIFY_ERROR_CLASS_CHANGE: {
PotentiallyMarkRuntimeThrow();
break;
diff --git a/runtime/verifier/verifier_enums.h b/runtime/verifier/verifier_enums.h
index 86775ff56d..1c90520155 100644
--- a/runtime/verifier/verifier_enums.h
+++ b/runtime/verifier/verifier_enums.h
@@ -81,9 +81,11 @@ enum VerifyError : uint32_t {
VERIFY_ERROR_ACCESS_METHOD = 1 << 7, // IllegalAccessError.
VERIFY_ERROR_CLASS_CHANGE = 1 << 8, // IncompatibleClassChangeError.
VERIFY_ERROR_INSTANTIATION = 1 << 9, // InstantiationError.
- VERIFY_ERROR_LOCKING = 1 << 10, // Could not guarantee balanced locking. This should
+ VERIFY_ERROR_FILLED_NEW_ARRAY = 1 << 10, // Unsupported `filled-new-array/-range` component
+ // type. Interpreter throws `InternalError.
+ VERIFY_ERROR_LOCKING = 1 << 11, // Could not guarantee balanced locking. This should
// be punted to the interpreter with access checks.
- VERIFY_ERROR_RUNTIME_THROW = 1 << 11, // The interpreter found an instruction that will
+ VERIFY_ERROR_RUNTIME_THROW = 1 << 12, // The interpreter found an instruction that will
// throw. Used for app compatibility for apps < T.
};
std::ostream& operator<<(std::ostream& os, VerifyError rhs);
diff --git a/test/412-new-array/smali/fill_array_data.smali b/test/412-new-array/smali/FillArrayData.smali
index f163084432..324ebcf916 100644
--- a/test/412-new-array/smali/fill_array_data.smali
+++ b/test/412-new-array/smali/FillArrayData.smali
@@ -1,3 +1,19 @@
+# /*
+# * Copyright 2014 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
.class public LFillArrayData;
.super Ljava/lang/Object;
diff --git a/test/412-new-array/smali/filled_new_array.smali b/test/412-new-array/smali/FilledNewArray.smali
index ed8683a14b..19c8487aa9 100644
--- a/test/412-new-array/smali/filled_new_array.smali
+++ b/test/412-new-array/smali/FilledNewArray.smali
@@ -1,3 +1,19 @@
+# /*
+# * Copyright 2014 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
.class public LFilledNewArray;
.super Ljava/lang/Object;
diff --git a/test/412-new-array/smali/FilledNewArrayInternalErrorB.smali b/test/412-new-array/smali/FilledNewArrayInternalErrorB.smali
new file mode 100644
index 0000000000..b5cf0e2a72
--- /dev/null
+++ b/test/412-new-array/smali/FilledNewArrayInternalErrorB.smali
@@ -0,0 +1,26 @@
+# /*
+# * Copyright 2025 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LFilledNewArrayInternalErrorB;
+
+.super Ljava/lang/Object;
+
+.method public static fail()[B
+ .registers 1
+ filled-new-array {}, [B
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/412-new-array/smali/FilledNewArrayInternalErrorC.smali b/test/412-new-array/smali/FilledNewArrayInternalErrorC.smali
new file mode 100644
index 0000000000..768fa9ff40
--- /dev/null
+++ b/test/412-new-array/smali/FilledNewArrayInternalErrorC.smali
@@ -0,0 +1,26 @@
+# /*
+# * Copyright 2025 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LFilledNewArrayInternalErrorC;
+
+.super Ljava/lang/Object;
+
+.method public static fail()[C
+ .registers 1
+ filled-new-array {}, [C
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/412-new-array/smali/FilledNewArrayInternalErrorF.smali b/test/412-new-array/smali/FilledNewArrayInternalErrorF.smali
new file mode 100644
index 0000000000..31056510bd
--- /dev/null
+++ b/test/412-new-array/smali/FilledNewArrayInternalErrorF.smali
@@ -0,0 +1,26 @@
+# /*
+# * Copyright 2025 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LFilledNewArrayInternalErrorF;
+
+.super Ljava/lang/Object;
+
+.method public static fail()[F
+ .registers 1
+ filled-new-array {}, [F
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/412-new-array/smali/FilledNewArrayInternalErrorS.smali b/test/412-new-array/smali/FilledNewArrayInternalErrorS.smali
new file mode 100644
index 0000000000..8394a8f1b9
--- /dev/null
+++ b/test/412-new-array/smali/FilledNewArrayInternalErrorS.smali
@@ -0,0 +1,26 @@
+# /*
+# * Copyright 2025 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LFilledNewArrayInternalErrorS;
+
+.super Ljava/lang/Object;
+
+.method public static fail()[S
+ .registers 1
+ filled-new-array {}, [S
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/412-new-array/smali/FilledNewArrayInternalErrorZ.smali b/test/412-new-array/smali/FilledNewArrayInternalErrorZ.smali
new file mode 100644
index 0000000000..2e7ae7e368
--- /dev/null
+++ b/test/412-new-array/smali/FilledNewArrayInternalErrorZ.smali
@@ -0,0 +1,26 @@
+# /*
+# * Copyright 2025 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LFilledNewArrayInternalErrorZ;
+
+.super Ljava/lang/Object;
+
+.method public static fail()[Z
+ .registers 1
+ filled-new-array {}, [Z
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/412-new-array/smali/FilledNewArrayVerifyError.smali b/test/412-new-array/smali/FilledNewArrayVerifyError.smali
new file mode 100644
index 0000000000..78b8076d9d
--- /dev/null
+++ b/test/412-new-array/smali/FilledNewArrayVerifyError.smali
@@ -0,0 +1,26 @@
+# /*
+# * Copyright 2014 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LFilledNewArrayVerifyError;
+
+.super Ljava/lang/Object;
+
+.method public static newRef(Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object;
+ .registers 3
+ filled-new-array {v1, v2}, [Ljava/lang/Integer;
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/412-new-array/smali/FilledNewArrayVerifyErrorD.smali b/test/412-new-array/smali/FilledNewArrayVerifyErrorD.smali
new file mode 100644
index 0000000000..4d44fdbbb2
--- /dev/null
+++ b/test/412-new-array/smali/FilledNewArrayVerifyErrorD.smali
@@ -0,0 +1,26 @@
+# /*
+# * Copyright 2025 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LFilledNewArrayVerifyErrorD;
+
+.super Ljava/lang/Object;
+
+.method public static fail()[D
+ .registers 1
+ filled-new-array {}, [D
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/412-new-array/smali/FilledNewArrayVerifyErrorJ.smali b/test/412-new-array/smali/FilledNewArrayVerifyErrorJ.smali
new file mode 100644
index 0000000000..7e8e2e5244
--- /dev/null
+++ b/test/412-new-array/smali/FilledNewArrayVerifyErrorJ.smali
@@ -0,0 +1,26 @@
+# /*
+# * Copyright 2025 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LFilledNewArrayVerifyErrorJ;
+
+.super Ljava/lang/Object;
+
+.method public static fail()[J
+ .registers 1
+ filled-new-array {}, [J
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/412-new-array/smali/filled_new_array_verify_error.smali b/test/412-new-array/smali/filled_new_array_verify_error.smali
deleted file mode 100644
index b1470ec612..0000000000
--- a/test/412-new-array/smali/filled_new_array_verify_error.smali
+++ /dev/null
@@ -1,10 +0,0 @@
-.class public LFilledNewArrayVerifyError;
-
-.super Ljava/lang/Object;
-
-.method public static newRef(Ljava/lang/Object;Ljava/lang/Object;)[Ljava/lang/Object;
- .registers 3
- filled-new-array {v1, v2}, [Ljava/lang/Integer;
- move-result-object v0
- return-object v0
-.end method
diff --git a/test/412-new-array/src/Main.java b/test/412-new-array/src/Main.java
index fb348ba9c9..c2f82908d7 100644
--- a/test/412-new-array/src/Main.java
+++ b/test/412-new-array/src/Main.java
@@ -29,7 +29,21 @@ public class Main extends TestCase {
testNegativeArraySize();
testSmaliFilledNewArray();
testSmaliFillArrayData();
- testSmaliVerifyError();
+
+ // Ensure the elements in filled-new-array must be assignable
+ // to the array component type.
+ testSmaliVerifyError("FilledNewArrayVerifyError");
+
+ // Ensure invalid array types `[J` and `[D` are rejected.
+ testSmaliVerifyError("FilledNewArrayVerifyErrorJ");
+ testSmaliVerifyError("FilledNewArrayVerifyErrorD");
+
+ // Test that `filled-new-array` with `[Z`, `B`, `[C`, `[S` or `[F` throws `InternalError`.
+ testSmaliInternalError("FilledNewArrayInternalErrorZ");
+ testSmaliInternalError("FilledNewArrayInternalErrorB");
+ testSmaliInternalError("FilledNewArrayInternalErrorC");
+ testSmaliInternalError("FilledNewArrayInternalErrorS");
+ testSmaliInternalError("FilledNewArrayInternalErrorF");
}
static void $opt$TestAllocations() {
@@ -205,18 +219,29 @@ public class Main extends TestCase {
}
}
- public static void testSmaliVerifyError() throws Exception {
+ public static void testSmaliVerifyError(String name) throws Exception {
Error error = null;
- // Ensure the elements in filled-new-array must be assignable
- // to the array component type.
try {
- Class.forName("FilledNewArrayVerifyError");
+ Class.forName(name);
} catch (VerifyError e) {
error = e;
}
assertNotNull(error);
}
+ public static void testSmaliInternalError(String name) throws Exception {
+ Throwable t = null;
+ try {
+ Class<?> c = Class.forName(name);
+ Method m = c.getMethod("fail");
+ m.invoke(null);
+ } catch (InvocationTargetException e) {
+ t = e.getCause();
+ assertTrue(t instanceof InternalError);
+ }
+ assertNotNull(t);
+ }
+
public static void testSmaliFillArrayData() throws Exception {
Class<?> c = Class.forName("FillArrayData");
{