Add disable/get/set_hidden_api_enforcement_policy extensions
Add JVMTI extensions to control the hidden-api enforcement policy. The
new extensions
com.android.art.misc.{disable,get,set}_hidden_api_enforcement_policy
use the int representations defined by ApplicationInfo.java. Users of
these APIs should be sure to toggle the hidden-api state temporarily
as needed and should avoid turning it off for long periods in order to
ensure users can continue to find bugs.
Test: ./test.py --host
Bug: 175329755
Change-Id: I89848faf6cf53179b713c6ca9f19f1fe8062b739
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index c22f38f..924a0d8 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -56,6 +56,7 @@
#include "gc/heap.h"
#include "gc_root.h"
#include "handle.h"
+#include "hidden_api.h"
#include "jni/jni_env_ext-inl.h"
#include "jni/jni_internal.h"
#include "mirror/array-alloc-inl.h"
@@ -1137,4 +1138,38 @@
return OK;
}
+jvmtiError ClassUtil::DisableHiddenApiEnforcementPolicy(jvmtiEnv* env) {
+ return SetHiddenApiEnforcementPolicy(
+ env, static_cast<jint>(art::hiddenapi::EnforcementPolicy::kDisabled));
+}
+
+jvmtiError ClassUtil::GetHiddenApiEnforcementPolicy(jvmtiEnv* env, jint* policy) {
+ if (env == nullptr) {
+ return ERR(INVALID_ENVIRONMENT);
+ } else if (art::Thread::Current() == nullptr) {
+ return ERR(UNATTACHED_THREAD);
+ } else if (policy == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ *policy = static_cast<jint>(art::Runtime::Current()->GetHiddenApiEnforcementPolicy());
+ return OK;
+}
+
+jvmtiError ClassUtil::SetHiddenApiEnforcementPolicy(jvmtiEnv* env, jint policy) {
+ if (env == nullptr) {
+ return ERR(INVALID_ENVIRONMENT);
+ } else if (art::Thread::Current() == nullptr) {
+ return ERR(UNATTACHED_THREAD);
+ } else if (policy < static_cast<jint>(art::hiddenapi::EnforcementPolicy::kDisabled) ||
+ policy > static_cast<jint>(art::hiddenapi::EnforcementPolicy::kMax)) {
+ JVMTI_LOG(INFO, env) << "Bad policy: " << policy << ", must be between "
+ << static_cast<jint>(art::hiddenapi::EnforcementPolicy::kDisabled)
+ << " and " << static_cast<jint>(art::hiddenapi::EnforcementPolicy::kMax);
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ art::Runtime::Current()->SetHiddenApiEnforcementPolicy(
+ static_cast<art::hiddenapi::EnforcementPolicy>(policy));
+ return OK;
+}
+
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_class.h b/openjdkjvmti/ti_class.h
index 7e427a0..229a6ce 100644
--- a/openjdkjvmti/ti_class.h
+++ b/openjdkjvmti/ti_class.h
@@ -93,6 +93,10 @@
static jvmtiError GetSourceDebugExtension(jvmtiEnv* env,
jclass klass,
char** source_debug_extension_ptr);
+
+ static jvmtiError DisableHiddenApiEnforcementPolicy(jvmtiEnv* env);
+ static jvmtiError GetHiddenApiEnforcementPolicy(jvmtiEnv* env, jint* policy);
+ static jvmtiError SetHiddenApiEnforcementPolicy(jvmtiEnv* env, jint policy);
};
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
index dd68533..10ea43a 100644
--- a/openjdkjvmti/ti_extension.cc
+++ b/openjdkjvmti/ti_extension.cc
@@ -509,6 +509,58 @@
if (error != ERR(NONE)) {
return error;
}
+ // GetHiddenApiEnforcementPolicy
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(ClassUtil::GetHiddenApiEnforcementPolicy),
+ "com.android.art.misc.get_hidden_api_enforcement_policy",
+ "Gets the current hiddenapi enforcement policy. Policy values are defined in"
+ " `frameworks/base/core/java/android/content/pm/ApplicationInfo.java` as the"
+ " HIDDEN_API_ENFORCEMENT_ static fields. See the comments in `art/runtime/hidden_api.h` for"
+ " more information. This should be used with"
+ " `com.android.art.misc.set_hidden_api_enforcement_policy` in order to restore the"
+ " hidden-api state after temporarily toggling it.",
+ {
+ { "policy", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false },
+ },
+ {
+ ERR(NULL_POINTER),
+ });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+ // SetHiddenApiEnforcementPolicy
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(ClassUtil::SetHiddenApiEnforcementPolicy),
+ "com.android.art.misc.set_hidden_api_enforcement_policy",
+ "Sets the hiddenapi enforcement policy to the given value. Policy values are defined in"
+ " `frameworks/base/core/java/android/content/pm/ApplicationInfo.java` as the"
+ " HIDDEN_API_ENFORCEMENT_ static fields. See the comments in `art/runtime/hidden_api.h` for"
+ " more information. This API should always be used sparingly and in conjunction with"
+ " `com.android.art.misc.get_hidden_api_enforcement_policy` to temporarily toggle"
+ " hidden-api on and off as changes are required.",
+ {
+ { "policy", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false },
+ },
+ {
+ ERR(ILLEGAL_ARGUMENT),
+ });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+ // DisableHiddenApiEnforcementPolicy
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(ClassUtil::DisableHiddenApiEnforcementPolicy),
+ "com.android.art.misc.disable_hidden_api_enforcement_policy",
+ "Sets the hiddenapi enforcement policy to disabled. This API should always be"
+ " used sparingly and in conjunction with"
+ " `com.android.art.misc.get_hidden_api_enforcement_policy` and"
+ " `com.android.art.misc.set_hidden_api_enforcement_policy` to temporarily"
+ " toggle hidden-api on and off as changes are required.",
+ {},
+ {});
+ if (error != ERR(NONE)) {
+ return error;
+ }
// Copy into output buffer.
diff --git a/test/2038-hiddenapi-jvmti-ext/build b/test/2038-hiddenapi-jvmti-ext/build
new file mode 100644
index 0000000..f4b029f
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/2038-hiddenapi-jvmti-ext/expected-stderr.txt b/test/2038-hiddenapi-jvmti-ext/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/expected-stderr.txt
diff --git a/test/2038-hiddenapi-jvmti-ext/expected-stdout.txt b/test/2038-hiddenapi-jvmti-ext/expected-stdout.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/2038-hiddenapi-jvmti-ext/hiddenapi-flags.csv b/test/2038-hiddenapi-jvmti-ext/hiddenapi-flags.csv
new file mode 100644
index 0000000..6b31be6
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/hiddenapi-flags.csv
@@ -0,0 +1,2 @@
+Lart/Test2038;->foo()V,blocked
+Lart/Test2038;->bar:I,blocked
diff --git a/test/2038-hiddenapi-jvmti-ext/hiddenapi_ext.cc b/test/2038-hiddenapi-jvmti-ext/hiddenapi_ext.cc
new file mode 100644
index 0000000..c012b11
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/hiddenapi_ext.cc
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <queue>
+#include <vector>
+
+#include "jvmti.h"
+
+// Test infrastructure
+#include "jvmti_helper.h"
+#include "nativehelper/scoped_local_ref.h"
+#include "nativehelper/scoped_primitive_array.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test2038HiddenApiExt {
+
+template <typename T>
+static void Dealloc(T* t) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(t));
+}
+
+template <typename T, typename... Rest>
+static void Dealloc(T* t, Rest... rs) {
+ Dealloc(t);
+ Dealloc(rs...);
+}
+
+static void DeallocParams(jvmtiParamInfo* params, jint n_params) {
+ for (jint i = 0; i < n_params; i++) {
+ Dealloc(params[i].name);
+ }
+}
+
+static constexpr std::string_view kDisablePolicyName =
+ "com.android.art.misc.disable_hidden_api_enforcement_policy";
+static constexpr std::string_view kGetPolicyName =
+ "com.android.art.misc.get_hidden_api_enforcement_policy";
+static constexpr std::string_view kSetPolicyName =
+ "com.android.art.misc.set_hidden_api_enforcement_policy";
+using GetPolicy = jvmtiError (*)(jvmtiEnv*, jint*);
+using SetPolicy = jvmtiError (*)(jvmtiEnv*, jint);
+using DisablePolicy = jvmtiError (*)(jvmtiEnv*);
+
+void* GetExtension(JNIEnv* env, const std::string_view& name) {
+ // Get the extensions.
+ jint n_ext = 0;
+ void* result = nullptr;
+ jvmtiExtensionFunctionInfo* infos = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetExtensionFunctions(&n_ext, &infos))) {
+ return nullptr;
+ }
+ for (jint i = 0; i < n_ext; i++) {
+ jvmtiExtensionFunctionInfo* cur_info = &infos[i];
+ if (name == std::string_view(cur_info->id)) {
+ result = reinterpret_cast<void*>(cur_info->func);
+ }
+ // Cleanup the cur_info
+ DeallocParams(cur_info->params, cur_info->param_count);
+ Dealloc(cur_info->id, cur_info->short_description, cur_info->params, cur_info->errors);
+ }
+ // Cleanup the array.
+ Dealloc(infos);
+ if (result == nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to find policy extensions.");
+ return nullptr;
+ }
+ return result;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_disablePolicy(JNIEnv* env, jclass) {
+ jint res;
+ GetPolicy get_policy = reinterpret_cast<GetPolicy>(GetExtension(env, kGetPolicyName));
+ if (get_policy == nullptr) {
+ return -1;
+ }
+ DisablePolicy disable_policy =
+ reinterpret_cast<DisablePolicy>(GetExtension(env, kDisablePolicyName));
+ if (disable_policy == nullptr) {
+ return -1;
+ }
+ if (JvmtiErrorToException(env, jvmti_env, get_policy(jvmti_env, &res))) {
+ return -1;
+ }
+ JvmtiErrorToException(env, jvmti_env, disable_policy(jvmti_env));
+ return res;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_setPolicy(JNIEnv* env, jclass, jint pol) {
+ jint res;
+ GetPolicy get_policy = reinterpret_cast<GetPolicy>(GetExtension(env, kGetPolicyName));
+ if (get_policy == nullptr) {
+ return -1;
+ }
+ SetPolicy set_policy = reinterpret_cast<SetPolicy>(GetExtension(env, kSetPolicyName));
+ if (set_policy == nullptr) {
+ return -1;
+ }
+ if (JvmtiErrorToException(env, jvmti_env, get_policy(jvmti_env, &res))) {
+ return -1;
+ }
+ JvmtiErrorToException(env, jvmti_env, set_policy(jvmti_env, pol));
+ return res;
+}
+
+} // namespace Test2038HiddenApiExt
+} // namespace art
diff --git a/test/2038-hiddenapi-jvmti-ext/info.txt b/test/2038-hiddenapi-jvmti-ext/info.txt
new file mode 100644
index 0000000..01c37e6
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/info.txt
@@ -0,0 +1 @@
+Tests that the JVMTI set/get_hidden_api_enforcement_policy extensions work
diff --git a/test/2038-hiddenapi-jvmti-ext/run b/test/2038-hiddenapi-jvmti-ext/run
new file mode 100755
index 0000000..c6e62ae
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+./default-run "$@" --jvmti
diff --git a/test/2038-hiddenapi-jvmti-ext/src-ex/Test2038.java b/test/2038-hiddenapi-jvmti-ext/src-ex/Test2038.java
new file mode 100644
index 0000000..07da43b
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/src-ex/Test2038.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package art;
+
+public class Test2038 {
+ public void foo() {
+ System.out.println("hello");
+ }
+
+ public int bar = 42;
+}
diff --git a/test/2038-hiddenapi-jvmti-ext/src/Main.java b/test/2038-hiddenapi-jvmti-ext/src/Main.java
new file mode 100644
index 0000000..b474c60
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/src/Main.java
@@ -0,0 +1,96 @@
+/*
+ * 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.io.File;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Main {
+ public static void main(String[] args) throws ClassNotFoundException {
+ System.loadLibrary(args[0]);
+
+ // Run the initialization routine. This will enable hidden API checks in
+ // the runtime, in case they are not enabled by default.
+ init();
+
+ // Load the '-ex' APK and attach it to the boot class path.
+ appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
+
+ // Find the test class in boot class loader and verify that its members are hidden.
+ Class<?> klass = Class.forName("art.Test2038", true, BOOT_CLASS_LOADER);
+ assertFieldIsHidden(klass, "before set-policy");
+ assertMethodIsHidden(klass, "before set-policy");
+
+ int old_policy = disablePolicy();
+
+ // Verify that the class members are not hidden.
+ assertFieldNotHidden(klass, "after disable-policy");
+ assertMethodNotHidden(klass, "after disable-policy");
+
+ setPolicy(old_policy);
+
+ assertFieldIsHidden(klass, "after set-policy 2");
+ assertMethodIsHidden(klass, "after set-policy 2");
+ }
+
+ private static void assertMethodNotHidden(Class<?> klass, String msg) {
+ try {
+ klass.getDeclaredMethod("foo");
+ } catch (NoSuchMethodException ex) {
+ // Unexpected. Should not have thrown NoSuchMethodException.
+ throw new RuntimeException("Method should be accessible " + msg);
+ }
+ }
+
+ private static void assertFieldNotHidden(Class<?> klass, String msg) {
+ try {
+ klass.getDeclaredField("bar");
+ } catch (NoSuchFieldException ex) {
+ // Unexpected. Should not have thrown NoSuchFieldException.
+ throw new RuntimeException("Field should be accessible " + msg);
+ }
+ }
+ private static void assertMethodIsHidden(Class<?> klass, String msg) {
+ try {
+ klass.getDeclaredMethod("foo");
+ // Unexpected. Should have thrown NoSuchMethodException.
+ throw new RuntimeException("Method should not be accessible " + msg);
+ } catch (NoSuchMethodException ex) {
+ }
+ }
+
+ private static void assertFieldIsHidden(Class<?> klass, String msg) {
+ try {
+ klass.getDeclaredField("bar");
+ // Unexpected. Should have thrown NoSuchFieldException.
+ throw new RuntimeException("Field should not be accessible " + msg);
+ } catch (NoSuchFieldException ex) {
+ }
+ }
+
+ private static final String DEX_EXTRA =
+ new File(System.getenv("DEX_LOCATION"), "2038-hiddenapi-jvmti-ext-ex.jar").getAbsolutePath();
+
+ private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
+
+ // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
+ private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
+ private static native void init();
+
+ // Native function implemented in hiddenapi_ext.cc
+ private static native int setPolicy(int new_policy);
+ private static native int disablePolicy();
+}
diff --git a/test/Android.bp b/test/Android.bp
index f7e85e9..5a458d2 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -462,6 +462,7 @@
"1963-add-to-dex-classloader-in-memory/check_memfd_create.cc",
"2012-structural-redefinition-failures-jni-id/set-jni-id-used.cc",
"2031-zygote-compiled-frame-deopt/native-wait.cc",
+ "2038-hiddenapi-jvmti-ext/hiddenapi_ext.cc",
],
static_libs: [
"libz",
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 385db45..ef4900b 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1183,7 +1183,8 @@
"2012-structural-redefinition-failures-jni-id",
"2033-shutdown-mechanics",
"2035-structural-native-method",
- "2036-structural-subclass-shadow"],
+ "2036-structural-subclass-shadow",
+ "2038-hiddenapi-jvmti-ext"],
"variant": "jvm",
"description": ["Doesn't run on RI."]
},