diff options
author | 2020-12-11 10:20:32 -0800 | |
---|---|---|
committer | 2021-01-11 16:26:39 +0000 | |
commit | c38d94449a0ccfefd396d2e01b571f3e804d9e48 (patch) | |
tree | 39b74f8fea3ccbafd95936333f87865c2289c263 | |
parent | 83881483515aec1dfaa0c848f1ce876c3f966b43 (diff) |
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
-rw-r--r-- | openjdkjvmti/ti_class.cc | 35 | ||||
-rw-r--r-- | openjdkjvmti/ti_class.h | 4 | ||||
-rw-r--r-- | openjdkjvmti/ti_extension.cc | 52 | ||||
-rw-r--r-- | test/2038-hiddenapi-jvmti-ext/build | 17 | ||||
-rw-r--r-- | test/2038-hiddenapi-jvmti-ext/expected-stderr.txt | 0 | ||||
-rw-r--r-- | test/2038-hiddenapi-jvmti-ext/expected-stdout.txt | 1 | ||||
-rw-r--r-- | test/2038-hiddenapi-jvmti-ext/hiddenapi-flags.csv | 2 | ||||
-rw-r--r-- | test/2038-hiddenapi-jvmti-ext/hiddenapi_ext.cc | 121 | ||||
-rw-r--r-- | test/2038-hiddenapi-jvmti-ext/info.txt | 1 | ||||
-rwxr-xr-x | test/2038-hiddenapi-jvmti-ext/run | 17 | ||||
-rw-r--r-- | test/2038-hiddenapi-jvmti-ext/src-ex/Test2038.java | 25 | ||||
-rw-r--r-- | test/2038-hiddenapi-jvmti-ext/src/Main.java | 96 | ||||
-rw-r--r-- | test/Android.bp | 1 | ||||
-rw-r--r-- | test/knownfailures.json | 3 |
14 files changed, 374 insertions, 1 deletions
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc index c22f38f576..924a0d84d7 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 @@ jvmtiError ClassUtil::GetSourceDebugExtension(jvmtiEnv* env, 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 7e427a06f2..229a6ce546 100644 --- a/openjdkjvmti/ti_class.h +++ b/openjdkjvmti/ti_class.h @@ -93,6 +93,10 @@ class ClassUtil { 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 dd68533523..10ea43a1ee 100644 --- a/openjdkjvmti/ti_extension.cc +++ b/openjdkjvmti/ti_extension.cc @@ -509,6 +509,58 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env, 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 0000000000..f4b029fb82 --- /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 0000000000..e69de29bb2 --- /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 0000000000..6a5618ebc6 --- /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 0000000000..6b31be671e --- /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 0000000000..c012b117ad --- /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 0000000000..01c37e6280 --- /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 0000000000..c6e62ae6cd --- /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 0000000000..07da43bc11 --- /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 0000000000..b474c602e2 --- /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 f7e85e95c7..5a458d2774 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -462,6 +462,7 @@ art_cc_defaults { "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 385db458a9..ef4900b42b 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."] }, |