From 989ab3b3034d16a57f5a893d73ed804169d8eced Mon Sep 17 00:00:00 2001 From: Roland Levillain Date: Wed, 18 May 2016 15:52:54 +0100 Subject: [PATCH] Catch classes inheriting from themselves in the class linker. (cherry picked from commit 90328ac545f65759a8e4fb217a75332906795518) Bug: 28685551 Bug: 27682580 Bug: 28830038 Change-Id: If568013bf3c82c1df9b282522712d9af5ca5945d --- runtime/class_linker.cc | 13 +++++ runtime/common_throws.cc | 7 +++ runtime/common_throws.h | 3 ++ test/804-class-extends-itself/expected.txt | 2 + test/804-class-extends-itself/info.txt | 1 + test/804-class-extends-itself/smali/Main.smali | 57 ++++++++++++++++++++++ .../smali/b_28685551.smali | 18 +++++++ 7 files changed, 101 insertions(+) create mode 100644 test/804-class-extends-itself/expected.txt create mode 100644 test/804-class-extends-itself/info.txt create mode 100644 test/804-class-extends-itself/smali/Main.smali create mode 100644 test/804-class-extends-itself/smali/b_28685551.smali diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 1835c728b..8fcb6b25f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -5316,6 +5316,19 @@ bool ClassLinker::LoadSuperAndInterfaces(Handle klass, const DexF const DexFile::ClassDef& class_def = dex_file.GetClassDef(klass->GetDexClassDefIndex()); uint16_t super_class_idx = class_def.superclass_idx_; if (super_class_idx != DexFile::kDexNoIndex16) { + // Check that a class does not inherit from itself directly. + // + // TODO: This is a cheap check to detect the straightforward case + // of a class extending itself (b/28685551), but we should do a + // proper cycle detection on loaded classes, to detect all cases + // of class circularity errors (b/28830038). + if (super_class_idx == class_def.class_idx_) { + ThrowClassCircularityError(klass.Get(), + "Class %s extends itself", + PrettyDescriptor(klass.Get()).c_str()); + return false; + } + mirror::Class* super_class = ResolveType(dex_file, super_class_idx, klass.Get()); if (super_class == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index f8e32c41b..75cce424e 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -137,6 +137,13 @@ void ThrowClassCircularityError(mirror::Class* c) { ThrowException("Ljava/lang/ClassCircularityError;", c, msg.str().c_str()); } +void ThrowClassCircularityError(mirror::Class* c, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + ThrowException("Ljava/lang/ClassCircularityError;", c, fmt, &args); + va_end(args); +} + // ClassFormatError void ThrowClassFormatError(mirror::Class* referrer, const char* fmt, ...) { diff --git a/runtime/common_throws.h b/runtime/common_throws.h index 39c4e52b1..c3a1f09db 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -58,6 +58,9 @@ void ThrowArrayStoreException(mirror::Class* element_class, mirror::Class* array void ThrowClassCircularityError(mirror::Class* c) SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR; +void ThrowClassCircularityError(mirror::Class* c, const char* fmt, ...) + SHARED_REQUIRES(Locks::mutator_lock_) COLD_ATTR; + // ClassCastException void ThrowClassCastException(mirror::Class* dest_type, mirror::Class* src_type) diff --git a/test/804-class-extends-itself/expected.txt b/test/804-class-extends-itself/expected.txt new file mode 100644 index 000000000..b98f963ce --- /dev/null +++ b/test/804-class-extends-itself/expected.txt @@ -0,0 +1,2 @@ +Caught ClassCircularityError +Done! diff --git a/test/804-class-extends-itself/info.txt b/test/804-class-extends-itself/info.txt new file mode 100644 index 000000000..c48934c21 --- /dev/null +++ b/test/804-class-extends-itself/info.txt @@ -0,0 +1 @@ +Exercise class linker check for classes extending themselves (b/28685551). diff --git a/test/804-class-extends-itself/smali/Main.smali b/test/804-class-extends-itself/smali/Main.smali new file mode 100644 index 000000000..5c349edcc --- /dev/null +++ b/test/804-class-extends-itself/smali/Main.smali @@ -0,0 +1,57 @@ +# Copyright (C) 2016 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. + +# We cannot implement Main in Java, as this would require to run +# dexmerger (to merge the Dex file produced from Smali code and the +# Dex file produced from Java code), which loops indefinitely when +# processing class B28685551, as this class inherits from itself. As +# a workaround, implement Main using Smali (we could also have used +# multidex, but this requires a custom build script). + +.class public LMain; +.super Ljava/lang/Object; + +.method public static main([Ljava/lang/String;)V + .registers 3 + .param p0, "args" + + invoke-static {}, LMain;->test()V + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + const-string v1, "Done!" + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + return-void +.end method + +.method static test()V + .registers 4 + + :try_start + const-string v2, "B28685551" + invoke-static {v2}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class; + :try_end + .catch Ljava/lang/ClassCircularityError; {:try_start .. :try_end} :catch + + move-result-object v0 + + :goto_7 + return-void + + :catch + move-exception v1 + .local v1, "e":Ljava/lang/ClassCircularityError; + sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream; + const-string v3, "Caught ClassCircularityError" + invoke-virtual {v2, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + goto :goto_7 +.end method diff --git a/test/804-class-extends-itself/smali/b_28685551.smali b/test/804-class-extends-itself/smali/b_28685551.smali new file mode 100644 index 000000000..d98c6e3b3 --- /dev/null +++ b/test/804-class-extends-itself/smali/b_28685551.smali @@ -0,0 +1,18 @@ +# Copyright (C) 2016 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. + +# Regression test for a class inheriting from itself. + +.class public LB28685551; +.super LB28685551; -- 2.11.0