diff options
author | 2025-02-13 14:54:16 +0000 | |
---|---|---|
committer | 2025-02-14 07:39:37 -0800 | |
commit | 8c80783c05b2767140f9229b523c2ce601f31ddf (patch) | |
tree | 722bebe84fa8581421f5e9f44398acab4a4b3453 | |
parent | ce10dc6fbdeaef31f0e7ff6e80e0678cb3e13e94 (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
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"); { |