diff options
| -rw-r--r-- | runtime/art_method.cc | 17 | ||||
| -rw-r--r-- | runtime/art_method.h | 4 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/api-blacklist.txt | 2 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/build | 17 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/expected.txt | 1 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/info.txt | 1 | ||||
| -rwxr-xr-x | test/999-redefine-hiddenapi/run | 17 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/src-ex/Test999.java | 25 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/src-redefine/art/Test999.java | 25 | ||||
| -rwxr-xr-x | test/999-redefine-hiddenapi/src-redefine/gen.sh | 30 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/src/Main.java | 111 | ||||
| -rw-r--r-- | test/999-redefine-hiddenapi/src/art/Redefinition.java | 91 | ||||
| -rwxr-xr-x | test/etc/default-build | 5 | ||||
| -rw-r--r-- | test/knownfailures.json | 11 |
14 files changed, 350 insertions, 7 deletions
diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 41b01c251b..7030e0657b 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -710,6 +710,23 @@ bool ArtMethod::HasAnyCompiledCode() { return GetOatMethodQuickCode(runtime->GetClassLinker()->GetImagePointerSize()) != nullptr; } +void ArtMethod::SetNotIntrinsic() { + if (!IsIntrinsic()) { + return; + } + + // Query the hidden API access flags of the intrinsic. + HiddenApiAccessFlags::ApiList intrinsic_api_list = GetHiddenApiAccessFlags(); + + // Clear intrinsic-related access flags. + ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits); + + // Re-apply hidden API access flags now that the method is not an intrinsic. + SetAccessFlags(HiddenApiAccessFlags::EncodeForRuntime(GetAccessFlags(), intrinsic_api_list)); + DCHECK_EQ(GetHiddenApiAccessFlags(), intrinsic_api_list); +} + + void ArtMethod::CopyFrom(ArtMethod* src, PointerSize image_pointer_size) { memcpy(reinterpret_cast<void*>(this), reinterpret_cast<const void*>(src), Size(image_pointer_size)); diff --git a/runtime/art_method.h b/runtime/art_method.h index acaa4a68a1..012d706756 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -194,9 +194,7 @@ class ArtMethod FINAL { return (GetAccessFlags() & kAccIntrinsicBits) >> kAccFlagsShift; } - void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_) { - ClearAccessFlags(kAccIntrinsic | kAccIntrinsicBits); - } + void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_); bool IsCopied() { static_assert((kAccCopied & (kAccIntrinsic | kAccIntrinsicBits)) == 0, diff --git a/test/999-redefine-hiddenapi/api-blacklist.txt b/test/999-redefine-hiddenapi/api-blacklist.txt new file mode 100644 index 0000000000..63e37aa757 --- /dev/null +++ b/test/999-redefine-hiddenapi/api-blacklist.txt @@ -0,0 +1,2 @@ +Lart/Test999;->foo()V +Lart/Test999;->bar:I diff --git a/test/999-redefine-hiddenapi/build b/test/999-redefine-hiddenapi/build new file mode 100644 index 0000000000..f4b029fb82 --- /dev/null +++ b/test/999-redefine-hiddenapi/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/999-redefine-hiddenapi/expected.txt b/test/999-redefine-hiddenapi/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/999-redefine-hiddenapi/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/999-redefine-hiddenapi/info.txt b/test/999-redefine-hiddenapi/info.txt new file mode 100644 index 0000000000..87bc30cf80 --- /dev/null +++ b/test/999-redefine-hiddenapi/info.txt @@ -0,0 +1 @@ +Tests that JVMTI class redefinition does not strip away hidden API access flags. diff --git a/test/999-redefine-hiddenapi/run b/test/999-redefine-hiddenapi/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/999-redefine-hiddenapi/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/999-redefine-hiddenapi/src-ex/Test999.java b/test/999-redefine-hiddenapi/src-ex/Test999.java new file mode 100644 index 0000000000..97495c5a47 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-ex/Test999.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 Test999 { + public void foo() { + System.out.println("hello"); + } + + public int bar = 42; +} diff --git a/test/999-redefine-hiddenapi/src-redefine/art/Test999.java b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java new file mode 100644 index 0000000000..c1b838ccc7 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-redefine/art/Test999.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 Test999 { + public void foo() { + System.out.println("Goodbye"); + } + + public int bar = 64; +} diff --git a/test/999-redefine-hiddenapi/src-redefine/gen.sh b/test/999-redefine-hiddenapi/src-redefine/gen.sh new file mode 100755 index 0000000000..6948cbbfc3 --- /dev/null +++ b/test/999-redefine-hiddenapi/src-redefine/gen.sh @@ -0,0 +1,30 @@ +#!/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. + +set -e +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +TMP=`mktemp -d` + +CLASS "art/Test999" + +(cd "$TMP" && javac -d "${TMP}" "$DIR/${CLASS}.java" && d8 --output . "$TMP/${CLASS}.class") + +echo ' private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(' +base64 "${TMP}/${CLASS}.class" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' +echo ' private static final byte[] DEX_BYTES = Base64.getDecoder().decode(' +base64 "${TMP}/classes.dex" | sed -E 's/^/ "/' | sed ':a;N;$!ba;s/\n/" +\n/g' | sed -E '$ s/$/");/' + +rm -rf "$TMP" diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java new file mode 100644 index 0000000000..c6365ac234 --- /dev/null +++ b/test/999-redefine-hiddenapi/src/Main.java @@ -0,0 +1,111 @@ +/* + * 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 Exception { + 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); + + // Find the test class in boot class loader and verify that its members are hidden. + Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER); + assertMethodIsHidden(klass, "before redefinition"); + assertFieldIsHidden(klass, "before redefinition"); + + // Redefine the class using JVMTI. + art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE); + art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES); + + // Verify that the class members are still hidden. + assertMethodIsHidden(klass, "after redefinition"); + assertFieldIsHidden(klass, "after redefinition"); + } + + private static void assertMethodIsHidden(Class<?> klass, String msg) throws Exception { + try { + klass.getDeclaredMethod("foo"); + // Unexpected. Should have thrown NoSuchMethodException. + throw new Exception("Method should not be accessible " + msg); + } catch (NoSuchMethodException ex) { + // Expected. + } + } + + private static void assertFieldIsHidden(Class<?> klass, String msg) throws Exception { + try { + klass.getDeclaredField("bar"); + // Unexpected. Should have thrown NoSuchFieldException. + throw new Exception("Field should not be accessible " + msg); + } catch (NoSuchFieldException ex) { + // Expected. + } + } + + private static final String DEX_EXTRA = + new File(System.getenv("DEX_LOCATION"), "999-redefine-hiddenapi-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); + private static native void init(); + + /** + * base64 encoded class/dex file for + * + * public class Test999 { + * public void foo() { + * System.out.println("Goodbye"); + * } + * + * public int bar = 64; + * } + */ + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADUAIAoABwARCQAGABIJABMAFAgAFQoAFgAXBwAYBwAZAQADYmFyAQABSQEABjxpbml0" + + "PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAANmb28BAApTb3VyY2VGaWxlAQAMVGVz" + + "dDk5OS5qYXZhDAAKAAsMAAgACQcAGgwAGwAcAQAHR29vZGJ5ZQcAHQwAHgAfAQALYXJ0L1Rlc3Q5" + + "OTkBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lv" + + "L1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xh" + + "bmcvU3RyaW5nOylWACEABgAHAAAAAQABAAgACQAAAAIAAQAKAAsAAQAMAAAAJwACAAEAAAALKrcA" + + "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" + + "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA="); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQD0dZ+IWxOi+cJDSWjfTnUerlZj1Lll3ONIAwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAQ" + + "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAUAgAANAEAAIYB" + + "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" + + "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" + + "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" + + "AAAAAAAAAAgAAAAAAAAAhwIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" + + "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" + + "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" + + "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" + + "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAFx+fkQ4eyJtaW4tYXBpIjox" + + "LCJzaGEtMSI6IjU2YzJlMzBmNTIzM2I4NDRmZjZkZGQ4N2ZiNTNkMzRmYjE3MjM3ZGYiLCJ2ZXJz" + + "aW9uIjoidjEuMi4xNS1kZXYifQAAAQEBAAEAgYAEtAIBAdQCAAAAAAAOAAAAAAAAAAEAAAAAAAAA" + + "AQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADMAAAABAAAAAIAAADkAAAABQAAAAQAAAD0" + + "AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIAAAB0AQAAARAAAAEAAACAAQAAAiAAABAA" + + "AACGAQAAACAAAAEAAACHAgAAAxAAAAEAAACYAgAAABAAAAEAAACcAgAA"); +} diff --git a/test/999-redefine-hiddenapi/src/art/Redefinition.java b/test/999-redefine-hiddenapi/src/art/Redefinition.java new file mode 100644 index 0000000000..1eec70b48c --- /dev/null +++ b/test/999-redefine-hiddenapi/src/art/Redefinition.java @@ -0,0 +1,91 @@ +/* + * 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; + +import java.util.ArrayList; +// Common Redefinition functions. Placed here for use by CTS +public class Redefinition { + public static final class CommonClassDefinition { + public final Class<?> target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } + } + + // A set of possible test configurations. Test should set this if they need to. + // This must be kept in sync with the defines in ti-agent/common_helper.cc + public static enum Config { + COMMON_REDEFINE(0), + COMMON_RETRANSFORM(1), + COMMON_TRANSFORM(2); + + private final int val; + private Config(int val) { + this.val = val; + } + } + + public static void setTestConfiguration(Config type) { + nativeSetTestConfiguration(type.val); + } + + private static native void nativeSetTestConfiguration(int type); + + // Transforms the class + public static native void doCommonClassRedefinition(Class<?> target, + byte[] classfile, + byte[] dexfile); + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) { + ArrayList<Class<?>> classes = new ArrayList<>(); + ArrayList<byte[]> class_files = new ArrayList<>(); + ArrayList<byte[]> dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static void addMultiTransformationResults(CommonClassDefinition... defs) { + for (CommonClassDefinition d : defs) { + addCommonTransformationResult(d.target.getCanonicalName(), + d.class_file_bytes, + d.dex_file_bytes); + } + } + + public static native void doCommonMultiClassRedefinition(Class<?>[] targets, + byte[][] classfiles, + byte[][] dexfiles); + public static native void doCommonClassRetransformation(Class<?>... target); + public static native void setPopRetransformations(boolean pop); + public static native void popTransformationFor(String name); + public static native void enableCommonRetransformation(boolean enable); + public static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/etc/default-build b/test/etc/default-build index 3e6577cfda..4d3ff8b26a 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -543,6 +543,11 @@ fi if [[ -d classes-ex ]] && [ ${NEED_DEX} = "true" ]; then make_dex classes-ex + # Apply hiddenapi on the dex files if the test has API list file(s). + if [ ${USE_HIDDENAPI} = "true" -a ${HAS_HIDDENAPI_SPEC} = "true" ]; then + make_hiddenapi classes-ex.dex + fi + # quick shuffle so that the stored name is "classes.dex" mv classes.dex classes-1.dex mv classes-ex.dex classes.dex diff --git a/test/knownfailures.json b/test/knownfailures.json index 436ab318ad..43f4c1d310 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -469,7 +469,8 @@ "674-hiddenapi", "649-vdex-duplicate-method", "804-class-extends-itself", - "921-hello-failure" + "921-hello-failure", + "999-redefine-hiddenapi" ], "description": [ "Tests that use illegal dex files or otherwise break dexter assumptions" @@ -486,7 +487,8 @@ "629-vdex-speed", "647-jni-get-field-id", "674-hiddenapi", - "944-transform-classloaders" + "944-transform-classloaders", + "999-redefine-hiddenapi" ], "description": [ "Tests that use custom class loaders or other features not supported ", @@ -895,7 +897,6 @@ "667-jit-jni-stub", "667-out-of-bounds", "668-aiobe", - "674-hiddenapi", "674-hotness-compiled", "674-vdex-uncompress", "675-checker-unverified-method", @@ -973,8 +974,10 @@ }, { "tests": ["616-cha-unloading", + "674-hiddenapi", "678-quickening", - "679-locks"], + "679-locks", + "999-redefine-hiddenapi"], "variant": "jvm", "description": ["Doesn't run on RI."] }, |