diff options
| -rw-r--r-- | runtime/native/java_lang_Class.cc | 34 | ||||
| -rw-r--r-- | test/674-hiddenapi/info.txt | 5 | ||||
| -rw-r--r-- | test/674-hiddenapi/src-ex/ChildClass.java | 74 | ||||
| -rw-r--r-- | test/674-hiddenapi/src-ex/JLI.java | 98 |
4 files changed, 190 insertions, 21 deletions
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index a8b203bff2..37724f0f14 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -33,6 +33,7 @@ #include "mirror/class_loader.h" #include "mirror/field-inl.h" #include "mirror/method.h" +#include "mirror/method_handles_lookup.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" @@ -49,12 +50,13 @@ namespace art { -// Returns true if the first non-ClassClass caller up the stack is in a platform dex file. +// Returns true if the first caller outside of the Class class or java.lang.invoke package +// is in a platform DEX file. static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - // Walk the stack and find the first frame not from java.lang.Class. + // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke. // This is very expensive. Save this till the last. - struct FirstNonClassClassCallerVisitor : public StackVisitor { - explicit FirstNonClassClassCallerVisitor(Thread* thread) + struct FirstExternalCallerVisitor : public StackVisitor { + explicit FirstExternalCallerVisitor(Thread* thread) : StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), caller(nullptr) { } @@ -68,18 +70,30 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l } else if (m->IsRuntimeMethod()) { // Internal runtime method, continue walking the stack. return true; - } else if (m->GetDeclaringClass()->IsClassClass()) { - return true; - } else { - caller = m; - return false; } + + ObjPtr<mirror::Class> declaring_class = m->GetDeclaringClass(); + if (declaring_class->IsBootStrapClassLoaded()) { + if (declaring_class->IsClassClass()) { + return true; + } + ObjPtr<mirror::Class> lookup_class = mirror::MethodHandlesLookup::StaticClass(); + if (declaring_class == lookup_class || declaring_class->IsInSamePackage(lookup_class)) { + // Check classes in the java.lang.invoke package. At the time of writing, the + // classes of interest are MethodHandles and MethodHandles.Lookup, but this + // is subject to change so conservatively cover the entire package. + return true; + } + } + + caller = m; + return false; } ArtMethod* caller; }; - FirstNonClassClassCallerVisitor visitor(self); + FirstExternalCallerVisitor visitor(self); visitor.WalkStack(); return visitor.caller != nullptr && hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass()); diff --git a/test/674-hiddenapi/info.txt b/test/674-hiddenapi/info.txt index 25ac6ae78f..94aac1e6d9 100644 --- a/test/674-hiddenapi/info.txt +++ b/test/674-hiddenapi/info.txt @@ -1,7 +1,8 @@ Test whether hidden API access flags are being enforced. The test is composed of two JARs. The first (parent) defines methods and fields and the second (child) -tries to access them with reflection/JNI or link against them. Note that the -first is compiled twice - once with and once without hidden access flags. +tries to access them with reflection/JNI/MethodHandles or link against them. +Note that the first is compiled twice - once with and once without hidden access +flags. The test then proceeds to exercise the following combinations of class loading: (a) Both parent and child dex loaded with PathClassLoader, parent's class loader diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index 71c92fba64..e3d3e698e6 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -15,13 +15,8 @@ */ import dalvik.system.VMRuntime; -import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.util.function.Consumer; public class ChildClass { @@ -211,6 +206,37 @@ public class ChildClass { throwDiscoveryException(klass, name, true, "JNI", canDiscover); } + // Test discovery with MethodHandles.lookup() which is caller + // context sensitive. + + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + if (JLI.canDiscoverWithLookupFindGetter(lookup, klass, name, int.class) + != canDiscover) { + throwDiscoveryException(klass, name, true, "MethodHandles.lookup().findGetter()", + canDiscover); + } + if (JLI.canDiscoverWithLookupFindStaticGetter(lookup, klass, name, int.class) + != canDiscover) { + throwDiscoveryException(klass, name, true, "MethodHandles.lookup().findStaticGetter()", + canDiscover); + } + + // Test discovery with MethodHandles.publicLookup() which can only + // see public fields. Looking up setters here and fields in + // interfaces are implicitly final. + + final MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + if (JLI.canDiscoverWithLookupFindSetter(publicLookup, klass, name, int.class) + != canDiscover) { + throwDiscoveryException(klass, name, true, "MethodHandles.publicLookup().findSetter()", + canDiscover); + } + if (JLI.canDiscoverWithLookupFindStaticSetter(publicLookup, klass, name, int.class) + != canDiscover) { + throwDiscoveryException(klass, name, true, "MethodHandles.publicLookup().findStaticSetter()", + canDiscover); + } + // Finish here if we could not discover the field. if (canDiscover) { @@ -298,7 +324,21 @@ public class ChildClass { throwDiscoveryException(klass, name, false, "JNI", canDiscover); } - // Finish here if we could not discover the field. + // Test discovery with MethodHandles.lookup(). + + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + final MethodType methodType = MethodType.methodType(int.class); + if (JLI.canDiscoverWithLookupFindVirtual(lookup, klass, name, methodType) != canDiscover) { + throwDiscoveryException(klass, name, false, "MethodHandles.lookup().findVirtual()", + canDiscover); + } + + if (JLI.canDiscoverWithLookupFindStatic(lookup, klass, name, methodType) != canDiscover) { + throwDiscoveryException(klass, name, false, "MethodHandles.lookup().findStatic()", + canDiscover); + } + + // Finish here if we could not discover the method. if (canDiscover) { // Test that modifiers are unaffected. @@ -354,6 +394,7 @@ public class ChildClass { hiddenness.mAssociatedType.mClass }; Object initargs[] = new Object[] { visibility.mAssociatedType.mDefaultValue, hiddenness.mAssociatedType.mDefaultValue }; + MethodType methodType = MethodType.methodType(void.class, args); boolean canDiscover = (behaviour != Behaviour.Denied); boolean setsWarning = (behaviour == Behaviour.Warning); @@ -384,7 +425,22 @@ public class ChildClass { throwDiscoveryException(klass, fullName, false, "JNI", canDiscover); } - // Finish here if we could not discover the field. + // Test discovery with MethodHandles.lookup() + + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + if (JLI.canDiscoverWithLookupFindConstructor(lookup, klass, methodType) != canDiscover) { + throwDiscoveryException(klass, fullName, false, "MethodHandles.lookup().findConstructor", + canDiscover); + } + + final MethodHandles.Lookup publicLookup = MethodHandles.publicLookup(); + if (JLI.canDiscoverWithLookupFindConstructor(publicLookup, klass, methodType) != canDiscover) { + throwDiscoveryException(klass, fullName, false, + "MethodHandles.publicLookup().findConstructor", + canDiscover); + } + + // Finish here if we could not discover the constructor. if (!canDiscover) { return; diff --git a/test/674-hiddenapi/src-ex/JLI.java b/test/674-hiddenapi/src-ex/JLI.java new file mode 100644 index 0000000000..e4ca3bc902 --- /dev/null +++ b/test/674-hiddenapi/src-ex/JLI.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 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. + */ + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +/** Class with helper methods for field and method lookups using java.lang.invoke. */ +public class JLI { + public static boolean canDiscoverWithLookupFindGetter( + MethodHandles.Lookup lookup, Class<?> klass, String fieldName, Class<?> fieldType) { + try { + return lookup.findGetter(klass, fieldName, fieldType) != null; + } catch (NoSuchFieldException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindSetter( + MethodHandles.Lookup lookup, Class<?> klass, String fieldName, Class<?> fieldType) { + try { + return lookup.findSetter(klass, fieldName, fieldType) != null; + } catch (NoSuchFieldException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindStaticGetter( + MethodHandles.Lookup lookup, Class<?> klass, String fieldName, Class<?> fieldType) { + try { + return lookup.findStaticGetter(klass, fieldName, fieldType) != null; + } catch (NoSuchFieldException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindStaticSetter( + MethodHandles.Lookup lookup, Class<?> klass, String fieldName, Class<?> fieldType) { + try { + return lookup.findStaticSetter(klass, fieldName, fieldType) != null; + } catch (NoSuchFieldException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindConstructor( + MethodHandles.Lookup lookup, Class<?> klass, MethodType methodType) { + try { + return lookup.findConstructor(klass, methodType) != null; + } catch (NoSuchMethodException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindVirtual( + MethodHandles.Lookup lookup, Class<?> klass, String methodName, MethodType methodType) { + try { + return lookup.findVirtual(klass, methodName, methodType) != null; + } catch (NoSuchMethodException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } + + public static boolean canDiscoverWithLookupFindStatic( + MethodHandles.Lookup lookup, Class<?> klass, String methodName, MethodType methodType) { + try { + return lookup.findStatic(klass, methodName, methodType) != null; + } catch (NoSuchMethodException e) { + return false; + } catch (IllegalAccessException e) { + return true; + } + } +} |