summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/hidden_api.cc6
-rw-r--r--runtime/native/java_lang_Class.cc31
-rw-r--r--test/674-hiddenapi/src-art/Main.java53
-rw-r--r--test/674-hiddenapi/src-ex/ChildClass.java16
4 files changed, 69 insertions, 37 deletions
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 02b4f5349d..98feb4d8ed 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -137,8 +137,10 @@ Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method)
// Avoid re-examining the exemption list next time.
// Note this results in no warning for the member, which seems like what one would expect.
// Exemptions effectively adds new members to the whitelist.
- member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
- member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
+ if (runtime->ShouldDedupeHiddenApiWarnings()) {
+ member->SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(
+ member->GetAccessFlags(), HiddenApiAccessFlags::kWhitelist));
+ }
return kAllow;
}
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 37724f0f14..a97a17d072 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -125,19 +125,20 @@ ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
// the criteria. Some reflection calls only return public members
// (public_only == true), some members should be hidden from non-boot class path
// callers (enforce_hidden_api == true).
+template<typename T>
ALWAYS_INLINE static bool IsDiscoverable(bool public_only,
bool enforce_hidden_api,
- uint32_t access_flags) {
- if (public_only && ((access_flags & kAccPublic) == 0)) {
- return false;
- }
-
- if (enforce_hidden_api &&
- hiddenapi::GetActionFromAccessFlags(access_flags) == hiddenapi::kDeny) {
+ T* member)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (public_only && ((member->GetAccessFlags() & kAccPublic) == 0)) {
return false;
}
- return true;
+ return hiddenapi::GetMemberAction(member,
+ nullptr,
+ [enforce_hidden_api] (Thread*) { return !enforce_hidden_api; },
+ hiddenapi::kNone)
+ != hiddenapi::kDeny;
}
ALWAYS_INLINE static inline ObjPtr<mirror::Class> DecodeClass(
@@ -266,12 +267,12 @@ static mirror::ObjectArray<mirror::Field>* GetDeclaredFields(
bool enforce_hidden_api = ShouldEnforceHiddenApi(self);
// Lets go subtract all the non discoverable fields.
for (ArtField& field : ifields) {
- if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+ if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
--array_size;
}
}
for (ArtField& field : sfields) {
- if (!IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+ if (!IsDiscoverable(public_only, enforce_hidden_api, &field)) {
--array_size;
}
}
@@ -282,7 +283,7 @@ static mirror::ObjectArray<mirror::Field>* GetDeclaredFields(
return nullptr;
}
for (ArtField& field : ifields) {
- if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+ if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -297,7 +298,7 @@ static mirror::ObjectArray<mirror::Field>* GetDeclaredFields(
}
}
for (ArtField& field : sfields) {
- if (IsDiscoverable(public_only, enforce_hidden_api, field.GetAccessFlags())) {
+ if (IsDiscoverable(public_only, enforce_hidden_api, &field)) {
auto* reflect_field = mirror::Field::CreateFromArtField<kRuntimePointerSize>(self,
&field,
force_resolve);
@@ -518,7 +519,7 @@ static ALWAYS_INLINE inline bool MethodMatchesConstructor(
DCHECK(m != nullptr);
return m->IsConstructor() &&
!m->IsStatic() &&
- IsDiscoverable(public_only, enforce_hidden_api, m->GetAccessFlags());
+ IsDiscoverable(public_only, enforce_hidden_api, m);
}
static jobjectArray Class_getDeclaredConstructorsInternal(
@@ -588,7 +589,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT
uint32_t modifiers = m.GetAccessFlags();
// Add non-constructor declared methods.
if ((modifiers & kAccConstructor) == 0 &&
- IsDiscoverable(public_only, enforce_hidden_api, modifiers)) {
+ IsDiscoverable(public_only, enforce_hidden_api, &m)) {
++num_methods;
}
}
@@ -602,7 +603,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT
for (ArtMethod& m : klass->GetDeclaredMethods(kRuntimePointerSize)) {
uint32_t modifiers = m.GetAccessFlags();
if ((modifiers & kAccConstructor) == 0 &&
- IsDiscoverable(public_only, enforce_hidden_api, modifiers)) {
+ IsDiscoverable(public_only, enforce_hidden_api, &m)) {
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
auto* method =
diff --git a/test/674-hiddenapi/src-art/Main.java b/test/674-hiddenapi/src-art/Main.java
index a808e946a9..782748c345 100644
--- a/test/674-hiddenapi/src-art/Main.java
+++ b/test/674-hiddenapi/src-art/Main.java
@@ -16,6 +16,7 @@
import dalvik.system.InMemoryDexClassLoader;
import dalvik.system.PathClassLoader;
+import dalvik.system.VMRuntime;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Constructor;
@@ -34,26 +35,41 @@ public class Main {
// Enable hidden API checks in case they are disabled by default.
init();
+ // TODO there are sequential depencies between these test cases, and bugs
+ // in the production code may lead to subsequent tests to erroneously pass,
+ // or test the wrong thing. We rely on not deduping hidden API warnings
+ // here for the same reasons), meaning the code under test and production
+ // code are running in different configurations. Each test should be run in
+ // a fresh process to ensure that they are working correcting and not
+ // accidentally interfering with eachother.
+
// Run test with both parent and child dex files loaded with class loaders.
// The expectation is that hidden members in parent should be visible to
// the child.
- doTest(false, false);
+ doTest(false, false, false);
doUnloading();
// Now append parent dex file to boot class path and run again. This time
- // the child dex file should not be able to access private APIs of the parent.
+ // the child dex file should not be able to access private APIs of the
+ // parent.
appendToBootClassLoader(DEX_PARENT_BOOT);
- doTest(true, false);
+ doTest(true, false, false);
+ doUnloading();
+
+ // Now run the same test again, but with the blacklist exmemptions list set
+ // to "L" which matches everything.
+ doTest(true, false, true);
doUnloading();
// And finally append to child to boot class path as well. With both in the
// boot class path, access should be granted.
appendToBootClassLoader(DEX_CHILD);
- doTest(true, true);
+ doTest(true, true, false);
doUnloading();
}
- private static void doTest(boolean parentInBoot, boolean childInBoot) throws Exception {
+ private static void doTest(boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis)
+ throws Exception {
// Load parent dex if it is not in boot class path.
ClassLoader parentLoader = null;
if (parentInBoot) {
@@ -78,12 +94,18 @@ public class Main {
// be loaded once, but for some reason even classes from a class loader
// cannot register their native methods against symbols in a shared library
// loaded by their parent class loader.
- String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot);
+ String nativeLibCopy = createNativeLibCopy(parentInBoot, childInBoot, whitelistAllApis);
+
+ if (whitelistAllApis) {
+ VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"});
+ }
// Invoke ChildClass.runTest
Class.forName("ChildClass", true, childLoader)
- .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE)
- .invoke(null, nativeLibCopy, parentInBoot, childInBoot);
+ .getDeclaredMethod("runTest", String.class, Boolean.TYPE, Boolean.TYPE, Boolean.TYPE)
+ .invoke(null, nativeLibCopy, parentInBoot, childInBoot, whitelistAllApis);
+
+ VMRuntime.getRuntime().setHiddenApiExemptions(new String[0]);
}
// Routine which tries to figure out the absolute path of our native library.
@@ -122,20 +144,21 @@ public class Main {
return buffer;
}
- // Copy native library to a new file with a unique name so it does not conflict
- // with other loaded instance of the same binary file.
- private static String createNativeLibCopy(boolean parentInBoot, boolean childInBoot)
- throws Exception {
+ // Copy native library to a new file with a unique name so it does not
+ // conflict with other loaded instance of the same binary file.
+ private static String createNativeLibCopy(
+ boolean parentInBoot, boolean childInBoot, boolean whitelistAllApis) throws Exception {
String tempFileName = System.mapLibraryName(
- "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0"));
+ "hiddenapitest_" + (parentInBoot ? "1" : "0") + (childInBoot ? "1" : "0") +
+ (whitelistAllApis ? "1" : "0"));
File tempFile = new File(System.getenv("DEX_LOCATION"), tempFileName);
Files.copy(new File(nativeLibFileName).toPath(), tempFile.toPath());
return tempFile.getAbsolutePath();
}
private static void doUnloading() {
- // Do multiple GCs to prevent rare flakiness if some other thread is keeping the
- // classloader live.
+ // Do multiple GCs to prevent rare flakiness if some other thread is
+ // keeping the classloader live.
for (int i = 0; i < 5; ++i) {
Runtime.getRuntime().gc();
}
diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java
index e3d3e698e6..da0c36f536 100644
--- a/test/674-hiddenapi/src-ex/ChildClass.java
+++ b/test/674-hiddenapi/src-ex/ChildClass.java
@@ -70,7 +70,7 @@ public class ChildClass {
private static final boolean booleanValues[] = new boolean[] { false, true };
public static void runTest(String libFileName, boolean expectedParentInBoot,
- boolean expectedChildInBoot) throws Exception {
+ boolean expectedChildInBoot, boolean everythingWhitelisted) throws Exception {
System.load(libFileName);
// Check expectations about loading into boot class path.
@@ -84,13 +84,15 @@ public class ChildClass {
throw new RuntimeException("Expected ChildClass " + (expectedChildInBoot ? "" : "not ") +
"in boot class path");
}
+ ChildClass.everythingWhitelisted = everythingWhitelisted;
boolean isSameBoot = (isParentInBoot == isChildInBoot);
// Run meaningful combinations of access flags.
for (Hiddenness hiddenness : Hiddenness.values()) {
final Behaviour expected;
- if (isSameBoot || hiddenness == Hiddenness.Whitelist) {
+ if (isSameBoot || hiddenness == Hiddenness.Whitelist ||
+ (hiddenness == Hiddenness.Blacklist && everythingWhitelisted)) {
expected = Behaviour.Granted;
} else if (hiddenness == Hiddenness.Blacklist) {
expected = Behaviour.Denied;
@@ -510,14 +512,16 @@ public class ChildClass {
String fn, boolean canAccess) {
throw new RuntimeException("Expected " + (isField ? "field " : "method ") + klass.getName() +
"." + name + " to " + (canAccess ? "" : "not ") + "be discoverable with " + fn + ". " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
+ "everythingWhitelisted = " + everythingWhitelisted);
}
private static void throwAccessException(Class<?> klass, String name, boolean isField,
String fn) {
throw new RuntimeException("Expected to be able to access " + (isField ? "field " : "method ") +
klass.getName() + "." + name + " using " + fn + ". " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
+ "everythingWhitelisted = " + everythingWhitelisted);
}
private static void throwWarningException(Class<?> klass, String name, boolean isField,
@@ -525,7 +529,8 @@ public class ChildClass {
throw new RuntimeException("Expected access to " + (isField ? "field " : "method ") +
klass.getName() + "." + name + " using " + fn + " to " + (setsWarning ? "" : "not ") +
"set the warning flag. " +
- "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot);
+ "isParentInBoot = " + isParentInBoot + ", " + "isChildInBoot = " + isChildInBoot + ", " +
+ "everythingWhitelisted = " + everythingWhitelisted);
}
private static void throwModifiersException(Class<?> klass, String name, boolean isField) {
@@ -535,6 +540,7 @@ public class ChildClass {
private static boolean isParentInBoot;
private static boolean isChildInBoot;
+ private static boolean everythingWhitelisted;
private static native boolean hasPendingWarning();
private static native void clearWarning();