diff options
-rw-r--r-- | build.xml | 3 | ||||
-rw-r--r-- | javassist.jar | bin | 729051 -> 740722 bytes | |||
-rw-r--r-- | src/main/javassist/ClassPool.java | 71 | ||||
-rw-r--r-- | src/main/javassist/CtClassType.java | 16 | ||||
-rw-r--r-- | src/main/javassist/util/proxy/DefineClassHelper.java | 240 | ||||
-rw-r--r-- | src/main/javassist/util/proxy/DefinePackageHelper.java | 179 | ||||
-rwxr-xr-x | src/main/javassist/util/proxy/SecurityActions.java | 232 | ||||
-rw-r--r-- | src/test/javassist/JvstTest.java | 8 | ||||
-rw-r--r-- | src/test/javassist/JvstTest5.java | 46 | ||||
-rw-r--r-- | src/test/test/javassist/proxy/TestSecuredPrivileged.java | 281 | ||||
-rw-r--r-- | src/test/test1/FieldInit.java | 7 | ||||
-rw-r--r-- | src/test/test5/InnerModifier2.java | 13 |
12 files changed, 903 insertions, 193 deletions
@@ -17,7 +17,7 @@ <property name="build.classes.dir" value="${build.dir}/classes"/> <property name="test.src.dir" value="${basedir}/src/test"/> <property name="test.build.dir" value="${build.dir}/test-classes"/> - <property name="test.run.dir" value="${basedir}/runtest"/> + <property name="test.run.dir" value="${build.dir}/runtest"/> <property name="test.reports.dir" value = "${build.dir}/test-output"/> <property name="run.dir" value="${build.classes.dir}"/> @@ -219,7 +219,6 @@ Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.</i>]]></bottom> <target name="clean"> <delete dir="html"/> - <delete dir="${test.run.dir}" /> <delete dir="${build.dir}"/> <delete file="${target.jar}"/> <delete file="${dist-version}.zip"/> diff --git a/javassist.jar b/javassist.jar Binary files differindex 915b45a6..78328e63 100644 --- a/javassist.jar +++ b/javassist.jar diff --git a/src/main/javassist/ClassPool.java b/src/main/javassist/ClassPool.java index 89a0067e..60055992 100644 --- a/src/main/javassist/ClassPool.java +++ b/src/main/javassist/ClassPool.java @@ -21,19 +21,16 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.lang.reflect.Method; import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.security.ProtectionDomain; -import java.util.Hashtable; -import java.util.Iterator; import java.util.ArrayList; import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; import javassist.bytecode.ClassFile; import javassist.bytecode.Descriptor; +import javassist.util.proxy.DefinePackageHelper; /** * A container of <code>CtClass</code> objects. @@ -69,28 +66,8 @@ import javassist.bytecode.Descriptor; * @see javassist.CtClass * @see javassist.ClassPath */ +@SuppressWarnings({"unchecked", "rawtypes"}) public class ClassPool { - private static java.lang.reflect.Method definePackage = null; - - static { - if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_9) - try { - AccessController.doPrivileged(new PrivilegedExceptionAction(){ - public Object run() throws Exception{ - Class cl = Class.forName("java.lang.ClassLoader"); - definePackage = cl.getDeclaredMethod("definePackage", - new Class[] { String.class, String.class, String.class, - String.class, String.class, String.class, - String.class, java.net.URL.class }); - return null; - } - }); - } - catch (PrivilegedActionException pae) { - throw new RuntimeException("cannot initialize ClassPool", - pae.getException()); - } - } /** * Determines the search order. @@ -321,7 +298,7 @@ public class ClassPool { * @see #importPackage(String) * @since 3.1 */ - public Iterator getImportedPackages() { + public Iterator<String> getImportedPackages() { return importedPackages.iterator(); } @@ -1175,43 +1152,7 @@ public class ClassPool { public void makePackage(ClassLoader loader, String name) throws CannotCompileException { - if (definePackage == null) - throw new CannotCompileException("give the JVM --add-opens"); - - Object[] args = new Object[] { - name, null, null, null, null, null, null, null }; - Throwable t; - try { - makePackage2(definePackage, loader, args); - return; - } - catch (java.lang.reflect.InvocationTargetException e) { - t = e.getTargetException(); - if (t == null) - t = e; - else if (t instanceof IllegalArgumentException) { - // if the package is already defined, an IllegalArgumentException - // is thrown. - return; - } - } - catch (Exception e) { - t = e; - } - - throw new CannotCompileException(t); + DefinePackageHelper.definePackage(name, loader); } - private static synchronized Object makePackage2(Method method, - ClassLoader loader, Object[] args) - throws Exception - { - method.setAccessible(true); - try { - return method.invoke(loader, args); - } - finally { - method.setAccessible(false); - } - } } diff --git a/src/main/javassist/CtClassType.java b/src/main/javassist/CtClassType.java index 0c2f775e..3791b5fb 100644 --- a/src/main/javassist/CtClassType.java +++ b/src/main/javassist/CtClassType.java @@ -446,9 +446,19 @@ class CtClassType extends CtClass { int acc = cf.getAccessFlags(); acc = AccessFlag.clear(acc, AccessFlag.SUPER); int inner = cf.getInnerAccessFlags(); - if (inner != -1 && (inner & AccessFlag.STATIC) != 0) - acc |= AccessFlag.STATIC; - + if (inner != -1) { + if ((inner & AccessFlag.STATIC) != 0) + acc |= AccessFlag.STATIC; + if ((inner & AccessFlag.PUBLIC) != 0) + acc |= AccessFlag.PUBLIC; + else { + acc &= ~AccessFlag.PUBLIC; //clear PUBLIC + if ((inner & AccessFlag.PROTECTED) != 0) + acc |= AccessFlag.PROTECTED; + else if ((inner & AccessFlag.PRIVATE) != 0) + acc |= AccessFlag.PRIVATE; + } + } return AccessFlag.toModifier(acc); } diff --git a/src/main/javassist/util/proxy/DefineClassHelper.java b/src/main/javassist/util/proxy/DefineClassHelper.java index 97c83800..3ed261e5 100644 --- a/src/main/javassist/util/proxy/DefineClassHelper.java +++ b/src/main/javassist/util/proxy/DefineClassHelper.java @@ -16,13 +16,11 @@ package javassist.util.proxy; -import java.io.IOException; -import java.lang.reflect.Method; -import java.lang.reflect.Field; -import java.security.ProtectionDomain; +import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; -import sun.misc.Unsafe; +import java.lang.reflect.Method; +import java.security.ProtectionDomain; import javassist.CannotCompileException; import javassist.bytecode.ClassFile; @@ -32,39 +30,153 @@ import javassist.bytecode.ClassFile; * * @since 3.22 */ -public class DefineClassHelper { - private static java.lang.reflect.Method defineClass1 = null; - private static java.lang.reflect.Method defineClass2 = null; - private static Unsafe sunMiscUnsafe = null; - - static { - if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_9) - try { - Class<?> cl = Class.forName("java.lang.ClassLoader"); - defineClass1 = SecurityActions.getDeclaredMethod( - cl, - "defineClass", - new Class[] { String.class, byte[].class, - int.class, int.class }); - - defineClass2 = SecurityActions.getDeclaredMethod( - cl, - "defineClass", - new Class[] { String.class, byte[].class, - int.class, int.class, ProtectionDomain.class }); +public class DefineClassHelper +{ + + private static enum SecuredPrivileged + { + JAVA_9 { + final class ReferencedUnsafe + { + private final SecurityActions.TheUnsafe sunMiscUnsafeTheUnsafe; + private final MethodHandle defineClass; + + ReferencedUnsafe(SecurityActions.TheUnsafe usf, MethodHandle meth) + { + this.sunMiscUnsafeTheUnsafe = usf; + this.defineClass = meth; + } + + Class<?> defineClass(String name, byte[] b, int off, int len, + ClassLoader loader, ProtectionDomain protectionDomain) + throws ClassFormatError { + if (stack.getCallerClass() != SecuredPrivileged.JAVA_9.getClass()) + throw new IllegalAccessError("Access denied for caller."); + try { + return (Class<?>) defineClass.invokeWithArguments( + sunMiscUnsafeTheUnsafe.theUnsafe, + name, b, off, len, loader, protectionDomain); + } catch (Throwable e) { + if (e instanceof RuntimeException) throw (RuntimeException) e; + if (e instanceof ClassFormatError) throw (ClassFormatError) e; + throw new ClassFormatError(e.getMessage()); + } + } + } + private final StackWalker stack = StackWalker + .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); + private final ReferencedUnsafe sunMiscUnsafe = getReferencedUnsafe(); + private final ReferencedUnsafe getReferencedUnsafe() + { + if (null != SecuredPrivileged.JAVA_9 + && stack.getCallerClass() != this.getClass()) + throw new IllegalAccessError("Access denied for caller."); + try { + SecurityActions.TheUnsafe usf = SecurityActions.getSunMiscUnsafeAnonymously(); + MethodHandle meth = MethodHandles.lookup() + .unreflect(usf.methods.get("defineClass").get(0)); + return new ReferencedUnsafe(usf, meth); + } catch (Throwable e) { + throw new RuntimeException("cannot initialize", e); + } } - catch (Exception e) { - throw new RuntimeException("cannot initialize"); + + @Override + public Class<?> defineClass(String name, byte[] b, int off, int len, + ClassLoader loader, ProtectionDomain protectionDomain) + throws ClassFormatError + { + if (stack.getCallerClass() != DefineClassHelper.class) + throw new IllegalAccessError("Access denied for caller."); + return sunMiscUnsafe.defineClass(name, b, off, len, loader, + protectionDomain); } - else - try { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - sunMiscUnsafe = (sun.misc.Unsafe)theUnsafe.get(null); + }, + JAVA_7 { + private final SecurityActions stack = SecurityActions.stack; + private final MethodHandle defineClass = getDefineClassMethodHandle(); + private final MethodHandle getDefineClassMethodHandle() + { + if (null != SecuredPrivileged.JAVA_7 + && stack.getCallerClass() != this.getClass()) + throw new IllegalAccessError("Access denied for caller."); + try { + return SecurityActions.getMethodHandle(ClassLoader.class, + "defineClass", new Class[] { + String.class, byte[].class, int.class, int.class, + ProtectionDomain.class + }); + } catch (NoSuchMethodException e) { + throw new RuntimeException("cannot initialize", e); + } } - catch (Throwable t) {} + + @Override + protected Class<?> defineClass(String name, byte[] b, int off, int len, + ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError + { + if (stack.getCallerClass() != DefineClassHelper.class) + throw new IllegalAccessError("Access denied for caller."); + try { + return (Class<?>) defineClass.invokeWithArguments( + loader, name, b, off, len, protectionDomain); + } catch (Throwable e) { + if (e instanceof RuntimeException) throw (RuntimeException) e; + if (e instanceof ClassFormatError) throw (ClassFormatError) e; + throw new ClassFormatError(e.getMessage()); + } + } + }, + JAVA_OTHER { + private final Method defineClass = getDefineClassMethod(); + private final SecurityActions stack = SecurityActions.stack; + private final Method getDefineClassMethod() { + if (null != SecuredPrivileged.JAVA_OTHER + && stack.getCallerClass() != this.getClass()) + throw new IllegalAccessError("Access denied for caller."); + try { + return SecurityActions.getDeclaredMethod(ClassLoader.class, + "defineClass", new Class[] { + String.class, byte[].class, int.class, int.class, ProtectionDomain.class + }); + } catch (NoSuchMethodException e) { + throw new RuntimeException("cannot initialize", e); + } + } + + @Override + protected Class<?> defineClass(String name, byte[] b, int off, int len, + ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError + { + if (stack.getCallerClass() != DefineClassHelper.class) + throw new IllegalAccessError("Access denied for caller."); + try { + SecurityActions.setAccessible(defineClass, true); + return (Class<?>) defineClass.invoke(loader, new Object[] { + name, b, off, len, protectionDomain + }); + } catch (Throwable e) { + if (e instanceof ClassFormatError) throw (ClassFormatError) e; + if (e instanceof RuntimeException) throw (RuntimeException) e; + throw new ClassFormatError(e.getMessage()); + } + finally { + SecurityActions.setAccessible(defineClass, false); + } + } + + }; + + protected abstract Class<?> defineClass(String name, byte[] b, int off, int len, + ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError; } + private static final SecuredPrivileged privileged = ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9 + ? SecuredPrivileged.JAVA_9 + : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7 + ? SecuredPrivileged.JAVA_7 + : SecuredPrivileged.JAVA_OTHER; + /** * Loads a class file by a given class loader. * @@ -84,15 +196,18 @@ public class DefineClassHelper { ProtectionDomain domain, byte[] bcode) throws CannotCompileException { - if (ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9) - if (sunMiscUnsafe != null) - try { - return sunMiscUnsafe.defineClass(className, bcode, 0, bcode.length, - loader, domain); - } - catch (Throwable t2) {} - - return toClass2(className, loader, domain, bcode); + try { + return privileged.defineClass(className, bcode, 0, bcode.length, loader, domain); + } + catch (RuntimeException e) { + throw e; + } + catch (ClassFormatError e) { + throw new CannotCompileException(e.getCause()); + } + catch (Exception e) { + throw new CannotCompileException(e); + } } /** @@ -113,44 +228,5 @@ public class DefineClassHelper { } } - private static Class<?> toClass2(String cname, ClassLoader loader, - ProtectionDomain domain, byte[] bcode) - throws CannotCompileException - { - try { - Method method; - Object[] args; - if (domain == null) { - method = defineClass1; - args = new Object[] { cname, bcode, Integer.valueOf(0), - Integer.valueOf(bcode.length) }; - } - else { - method = defineClass2; - args = new Object[] { cname, bcode, Integer.valueOf(0), - Integer.valueOf(bcode.length), domain }; - } - - return toClass3(method, loader, args); - } - catch (RuntimeException e) { - throw e; - } - catch (java.lang.reflect.InvocationTargetException e) { - throw new CannotCompileException(e.getTargetException()); - } - catch (Exception e) { - throw new CannotCompileException(e); - } - } - - private static synchronized - Class<?> toClass3(Method method, ClassLoader loader, Object[] args) - throws Exception - { - SecurityActions.setAccessible(method, true); - Class<?> clazz = (Class<?>)method.invoke(loader, args); - SecurityActions.setAccessible(method, false); - return clazz; - } + private DefineClassHelper() {} } diff --git a/src/main/javassist/util/proxy/DefinePackageHelper.java b/src/main/javassist/util/proxy/DefinePackageHelper.java new file mode 100644 index 00000000..1fe0bd61 --- /dev/null +++ b/src/main/javassist/util/proxy/DefinePackageHelper.java @@ -0,0 +1,179 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later, + * or the Apache License Version 2.0. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package javassist.util.proxy; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; + +import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.bytecode.ClassFile; + +/** + * Helper class for invoking {@link ClassLoader#defineClass(String,byte[],int,int)}. + * + * @since 3.22 + */ +public class DefinePackageHelper +{ + + private static enum SecuredPrivileged + { + JAVA_9 { + // definePackage has been discontinued for JAVA 9 + @Override + protected Package definePackage(ClassLoader loader, String name, String specTitle, + String specVersion, String specVendor, String implTitle, String implVersion, + String implVendor, URL sealBase) throws IllegalArgumentException + { + throw new RuntimeException("define package has been disabled for jigsaw"); + } + }, + JAVA_7 { + private final SecurityActions stack = SecurityActions.stack; + private final MethodHandle definePackage = getDefinePackageMethodHandle(); + private MethodHandle getDefinePackageMethodHandle() + { + if (stack.getCallerClass() != this.getClass()) + throw new IllegalAccessError("Access denied for caller."); + try { + return SecurityActions.getMethodHandle(ClassLoader.class, + "definePackage", new Class[] { + String.class, String.class, String.class, String.class, + String.class, String.class, String.class, URL.class + }); + } catch (NoSuchMethodException e) { + throw new RuntimeException("cannot initialize", e); + } + } + + @Override + protected Package definePackage(ClassLoader loader, String name, String specTitle, + String specVersion, String specVendor, String implTitle, String implVersion, + String implVendor, URL sealBase) throws IllegalArgumentException { + if (stack.getCallerClass() != DefinePackageHelper.class) + throw new IllegalAccessError("Access denied for caller."); + try { + return (Package) definePackage.invokeWithArguments(loader, name, specTitle, + specVersion, specVendor, implTitle, implVersion, implVendor, sealBase); + } catch (Throwable e) { + if (e instanceof IllegalArgumentException) throw (IllegalArgumentException) e; + if (e instanceof RuntimeException) throw (RuntimeException) e; + } + return null; + } + }, + JAVA_OTHER { + private final SecurityActions stack = SecurityActions.stack; + private final Method definePackage = getDefinePackageMethod(); + private Method getDefinePackageMethod() + { + if (stack.getCallerClass() != this.getClass()) + throw new IllegalAccessError("Access denied for caller."); + try { + return SecurityActions.getDeclaredMethod(ClassLoader.class, + "definePackage", new Class[] { + String.class, String.class, String.class, String.class, + String.class, String.class, String.class, URL.class + }); + } catch (NoSuchMethodException e) { + throw new RuntimeException("cannot initialize", e); + } + } + + @Override + protected Package definePackage(ClassLoader loader, String name, String specTitle, + String specVersion, String specVendor, String implTitle, String implVersion, + String implVendor, URL sealBase) throws IllegalArgumentException + { + if (stack.getCallerClass() != DefinePackageHelper.class) + throw new IllegalAccessError("Access denied for caller."); + try { + definePackage.setAccessible(true); + return (Package) definePackage.invoke(loader, new Object[] { + name, specTitle, specVersion, specVendor, implTitle, + implVersion, implVendor, sealBase + }); + } catch (Throwable e) { + if (e instanceof InvocationTargetException) { + Throwable t = ((InvocationTargetException) e).getTargetException(); + if (t instanceof IllegalArgumentException) + throw (IllegalArgumentException) t; + } + if (e instanceof RuntimeException) throw (RuntimeException) e; + } + finally { + definePackage.setAccessible(false); + } + return null; + } + }; + + protected abstract Package definePackage(ClassLoader loader, String name, String specTitle, + String specVersion, String specVendor, String implTitle, String implVersion, + String implVendor, URL sealBase) throws IllegalArgumentException; + } + + private static final SecuredPrivileged privileged = ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9 + ? SecuredPrivileged.JAVA_9 + : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7 + ? SecuredPrivileged.JAVA_7 + : SecuredPrivileged.JAVA_OTHER; + + + /** + * Defines a new package. If the package is already defined, this method + * performs nothing. + * + * <p>You do not necessarily need to + * call this method. If this method is called, then + * <code>getPackage()</code> on the <code>Class</code> object returned + * by <code>toClass()</code> will return a non-null object.</p> + * + * <p>The jigsaw module introduced by Java 9 has broken this method. + * In Java 9 or later, the VM argument + * <code>--add-opens java.base/java.lang=ALL-UNNAMED</code> + * has to be given to the JVM so that this method can run. + * </p> + * + * @param loader the class loader passed to <code>toClass()</code> or + * the default one obtained by <code>getClassLoader()</code>. + * @param name the package name. + * @see #getClassLoader() + * @see #toClass(CtClass) + * @see CtClass#toClass() */ + public static void definePackage(String className, ClassLoader loader) + throws CannotCompileException + { + try { + privileged.definePackage(loader, className, + null, null, null, null, null, null, null); + } + catch (IllegalArgumentException e) { + // if the package is already defined, an IllegalArgumentException + // is thrown. + return; + } + catch (Exception e) { + throw new CannotCompileException(e); + } + } + + private DefinePackageHelper() {} +} diff --git a/src/main/javassist/util/proxy/SecurityActions.java b/src/main/javassist/util/proxy/SecurityActions.java index ed1ec981..acb89c8d 100755 --- a/src/main/javassist/util/proxy/SecurityActions.java +++ b/src/main/javassist/util/proxy/SecurityActions.java @@ -15,6 +15,8 @@ */ package javassist.util.proxy; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -23,46 +25,99 @@ import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; -class SecurityActions { - static Method[] getDeclaredMethods(final Class clazz) { +import javassist.bytecode.ClassFile; + +class SecurityActions extends SecurityManager +{ + public static final SecurityActions stack = new SecurityActions(); + /** + * Since Java 9 abruptly removed <code>Reflection.getCallerClass()</code> + * in favour of <code>StackWalker</code> we are left having to find a + * solution for the older versions without upsetting the new compiler. + * + * The member scoped function <code>getClassContext()</code> + * available as a <code>SecurityManager</code> sibling remains + * functional across all versions, for now. + * + * @return represents the declaring class of the method that invoked + * the method that called this or idx 2 on the stack trace. + * @since 3.23 */ + public Class<?> getCallerClass() + { + return getClassContext()[2]; + } + + + static Method[] getDeclaredMethods(final Class<?> clazz) + { if (System.getSecurityManager() == null) return clazz.getDeclaredMethods(); else { - return (Method[]) AccessController - .doPrivileged(new PrivilegedAction() { - public Object run() { - return clazz.getDeclaredMethods(); - } - }); + return AccessController.doPrivileged( + new PrivilegedAction<Method[]>() { + public Method[] run() { + return clazz.getDeclaredMethods(); + } + }); } } - static Constructor[] getDeclaredConstructors(final Class clazz) { + static Constructor<?>[] getDeclaredConstructors(final Class<?> clazz) + { if (System.getSecurityManager() == null) return clazz.getDeclaredConstructors(); else { - return (Constructor[]) AccessController - .doPrivileged(new PrivilegedAction() { - public Object run() { - return clazz.getDeclaredConstructors(); - } - }); + return AccessController.doPrivileged( + new PrivilegedAction<Constructor<?>[]>() { + public Constructor<?>[] run() { + return clazz.getDeclaredConstructors(); + } + }); } } - static Method getDeclaredMethod(final Class clazz, final String name, - final Class[] types) throws NoSuchMethodException { + static MethodHandle getMethodHandle(final Class<?> clazz, final + String name, final Class<?>[] params) throws NoSuchMethodException + { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction<MethodHandle>() { + public MethodHandle run() throws IllegalAccessException, + NoSuchMethodException, SecurityException { + Method rmet = clazz.getDeclaredMethod(name, params); + rmet.setAccessible(true); + MethodHandle meth = MethodHandles.lookup().unreflect(rmet); + rmet.setAccessible(false); + return meth; + } + }); + } + catch (PrivilegedActionException e) { + if (e.getCause() instanceof NoSuchMethodException) + throw (NoSuchMethodException) e.getCause(); + throw new RuntimeException(e.getCause()); + } + } + + static Method getDeclaredMethod(final Class<?> clazz, final String name, + final Class<?>[] types) throws NoSuchMethodException + { if (System.getSecurityManager() == null) return clazz.getDeclaredMethod(name, types); else { try { - return (Method) AccessController - .doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws Exception { - return clazz.getDeclaredMethod(name, types); - } - }); + return AccessController.doPrivileged( + new PrivilegedExceptionAction<Method>() { + public Method run() throws Exception { + return clazz.getDeclaredMethod(name, types); + } + }); } catch (PrivilegedActionException e) { if (e.getCause() instanceof NoSuchMethodException) @@ -73,20 +128,20 @@ class SecurityActions { } } - static Constructor getDeclaredConstructor(final Class clazz, - final Class[] types) + static Constructor<?> getDeclaredConstructor(final Class<?> clazz, + final Class<?>[] types) throws NoSuchMethodException { if (System.getSecurityManager() == null) return clazz.getDeclaredConstructor(types); else { try { - return (Constructor) AccessController - .doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws Exception { - return clazz.getDeclaredConstructor(types); - } - }); + return AccessController.doPrivileged( + new PrivilegedExceptionAction<Constructor<?>>() { + public Constructor<?> run() throws Exception { + return clazz.getDeclaredConstructor(types); + } + }); } catch (PrivilegedActionException e) { if (e.getCause() instanceof NoSuchMethodException) @@ -98,12 +153,13 @@ class SecurityActions { } static void setAccessible(final AccessibleObject ao, - final boolean accessible) { + final boolean accessible) + { if (System.getSecurityManager() == null) ao.setAccessible(accessible); else { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { + AccessController.doPrivileged(new PrivilegedAction<Void>() { + public Void run() { ao.setAccessible(accessible); return null; } @@ -118,19 +174,113 @@ class SecurityActions { fld.set(target, value); else { try { - AccessController.doPrivileged(new PrivilegedExceptionAction() { - public Object run() throws Exception { - fld.set(target, value); - return null; - } - }); + AccessController.doPrivileged( + new PrivilegedExceptionAction<Void>() { + public Void run() throws Exception { + fld.set(target, value); + return null; + } + }); } catch (PrivilegedActionException e) { if (e.getCause() instanceof NoSuchMethodException) throw (IllegalAccessException) e.getCause(); - throw new RuntimeException(e.getCause()); } } } + + static TheUnsafe getSunMiscUnsafeAnonymously() throws ClassNotFoundException + { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction<TheUnsafe>() { public TheUnsafe run() throws + ClassNotFoundException, NoSuchFieldException, SecurityException, + IllegalArgumentException, IllegalAccessException { + Class<?> unsafe = Class.forName("sun.misc.Unsafe"); + Field theUnsafe = unsafe.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + TheUnsafe usf = stack.new TheUnsafe(unsafe, theUnsafe.get(null)); + theUnsafe.setAccessible(false); + disableWarning(usf); + return usf; + } + }); + } + catch (PrivilegedActionException e) { + if (e.getCause() instanceof ClassNotFoundException) + throw (ClassNotFoundException) e.getCause(); + if (e.getCause() instanceof NoSuchFieldException) + throw new ClassNotFoundException("No such instance.", e.getCause()); + if (e.getCause() instanceof IllegalAccessException + || e.getCause() instanceof IllegalAccessException + || e.getCause() instanceof SecurityException) + throw new ClassNotFoundException("Security denied access.", e.getCause()); + throw new RuntimeException(e.getCause()); + } + } + /** + * _The_ Notorious sun.misc.Unsafe in all its glory, but anonymous + * so as not to attract unwanted attention. Kept in two separate + * parts it manages to avoid detection from linker/compiler/general + * complainers and those. This functionality will vanish from the + * JDK soon but in the meantime it shouldn't be an obstacle. + * + * All exposed methods are cached in a dictionary with overloaded + * methods collected under their corresponding keys. Currently the + * implementation assumes there is only one, if you need find a + * need there will have to be a compare. + * @since 3.23 */ + class TheUnsafe + { + final Class<?> unsafe; + final Object theUnsafe; + final Map<String, List<Method>> methods = + new HashMap<String, List<Method>>(); + + TheUnsafe(Class<?> c, Object o) + { + this.unsafe = c; + this.theUnsafe = o; + for (Method m: unsafe.getDeclaredMethods()) { + if (!methods.containsKey(m.getName())) { + methods.put(m.getName(), Collections.singletonList(m)); + continue; + } + if (methods.get(m.getName()).size() == 1) + methods.put(m.getName(), + new ArrayList<Method>(methods.get(m.getName()))); + methods.get(m.getName()).add(m); + } + } + + private Method getM(String name, Object[] o) + { + return methods.get(name).get(0); + } + + public Object call(String name, Object... args) + { + try { + return getM(name, args).invoke(theUnsafe, args); + } catch (Throwable t) {t.printStackTrace();} + return null; + } + } + /** + * Java 9 now complains about every privileged action regardless. + * Displaying warnings of "illegal usage" and then instructing users + * to go hassle the maintainers in order to have it fixed. + * Making it hush for now, see all fixed. + * @param tu theUnsafe that'll fix it */ + static void disableWarning(TheUnsafe tu) { + try { + if (ClassFile.MAJOR_VERSION < ClassFile.JAVA_9) + return; + Class<?> cls = Class.forName("jdk.internal.module.IllegalAccessLogger"); + Field logger = cls.getDeclaredField("logger"); + tu.call("putObjectVolatile", cls, tu.call("staticFieldOffset", logger), null); + } catch (Exception e) { /*swallow*/ } + } } + diff --git a/src/test/javassist/JvstTest.java b/src/test/javassist/JvstTest.java index c6825fd0..d28c0654 100644 --- a/src/test/javassist/JvstTest.java +++ b/src/test/javassist/JvstTest.java @@ -103,6 +103,10 @@ public class JvstTest extends JvstTestRoot { cc.addField(f2); CtField f3 = CtField.make("public int f3;", cc); cc.addField(f3); + CtField f4 = CtField.make("public int f4 = this.f2 + 3;", cc); + cc.addField(f4); + CtField fi = CtField.make("public test1.FieldInit.FI fi = new test1.FieldInit.FI(this);", cc); + cc.addField(fi); testFieldInitHash = f1.hashCode(); cc.writeFile(); Object obj = make(cc.getName()); @@ -112,6 +116,10 @@ public class JvstTest extends JvstTestRoot { assertEquals(3, value2); int value3 = obj.getClass().getField("f3").getInt(obj); assertEquals(0, value3); + int value4 = obj.getClass().getField("f4").getInt(obj); + assertEquals(6, value4); + Object obfi = obj.getClass().getField("fi").get(obj); + assertTrue(obfi.getClass().getField("fi").get(obfi) == obj); } /* test CodeIterator.insertExGap(). diff --git a/src/test/javassist/JvstTest5.java b/src/test/javassist/JvstTest5.java index 578abfb2..346263cb 100644 --- a/src/test/javassist/JvstTest5.java +++ b/src/test/javassist/JvstTest5.java @@ -344,6 +344,52 @@ public class JvstTest5 extends JvstTestRoot { assertEquals(Modifier.PUBLIC, ica4.accessFlags(i5)); } + public void testInnerClassModifiers2() throws Exception { + CtClass cc = sloader.get("test5.InnerModifier2$Protected"); + Class<?> ccc = Class.forName("test5.InnerModifier2$Protected"); + assertEquals(cc.getModifiers(), ccc.getModifiers()); + assertTrue(Modifier.isProtected(cc.getModifiers())); + + cc = sloader.get("test5.InnerModifier2$Public"); + ccc = Class.forName("test5.InnerModifier2$Public"); + assertEquals(cc.getModifiers(), ccc.getModifiers()); + assertTrue(Modifier.isPublic(cc.getModifiers())); + + cc = sloader.get("test5.InnerModifier2$Private"); + ccc = Class.forName("test5.InnerModifier2$Private"); + assertEquals(cc.getModifiers(), ccc.getModifiers()); + assertTrue(Modifier.isPrivate(cc.getModifiers())); + + cc = sloader.get("test5.InnerModifier2$Package"); + ccc = Class.forName("test5.InnerModifier2$Package"); + assertEquals(cc.getModifiers(), ccc.getModifiers()); + assertTrue(Modifier.isPackage(cc.getModifiers())); + + cc = sloader.get("test5.InnerModifier2$ProtectedStatic"); + ccc = Class.forName("test5.InnerModifier2$ProtectedStatic"); + assertEquals(cc.getModifiers(), ccc.getModifiers()); + assertTrue(Modifier.isProtected(cc.getModifiers())); + assertTrue(Modifier.isStatic(cc.getModifiers())); + + cc = sloader.get("test5.InnerModifier2$PublicStatic"); + ccc = Class.forName("test5.InnerModifier2$PublicStatic"); + assertEquals(cc.getModifiers(), ccc.getModifiers()); + assertTrue(Modifier.isPublic(cc.getModifiers())); + assertTrue(Modifier.isStatic(cc.getModifiers())); + + cc = sloader.get("test5.InnerModifier2$PrivateStatic"); + ccc = Class.forName("test5.InnerModifier2$PrivateStatic"); + assertEquals(cc.getModifiers(), ccc.getModifiers()); + assertTrue(Modifier.isPrivate(cc.getModifiers())); + assertTrue(Modifier.isStatic(cc.getModifiers())); + + cc = sloader.get("test5.InnerModifier2$PackageStatic"); + ccc = Class.forName("test5.InnerModifier2$PackageStatic"); + assertEquals(cc.getModifiers(), ccc.getModifiers()); + assertTrue(Modifier.isPackage(cc.getModifiers())); + assertTrue(Modifier.isStatic(cc.getModifiers())); + } + private InnerClassesAttribute getInnerClassAttr(CtClass cc) { return (InnerClassesAttribute)cc.getClassFile2().getAttribute(InnerClassesAttribute.tag); } diff --git a/src/test/test/javassist/proxy/TestSecuredPrivileged.java b/src/test/test/javassist/proxy/TestSecuredPrivileged.java new file mode 100644 index 00000000..c51555f5 --- /dev/null +++ b/src/test/test/javassist/proxy/TestSecuredPrivileged.java @@ -0,0 +1,281 @@ +package test.javassist.proxy; +import static org.hamcrest.Matchers.arrayWithSize; +import static org.hamcrest.Matchers.both; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; +import static org.hamcrest.Matchers.stringContainsInOrder; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.ProtectionDomain; +import java.util.Arrays; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import javassist.ClassPool; +import javassist.CtClass; +import javassist.util.proxy.DefineClassHelper; + +public class TestSecuredPrivileged { + + public TestSecuredPrivileged() { + } + + @Rule + public ExpectedException thrown = ExpectedException.none(); + /** + * Test proves that you cannot even access members with + * private static and final modifiers. */ + @Test + public void testDefinedHelperPrivilegedFieldVisibility() { + try { + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + assertTrue(Modifier.isStatic(privi.getModifiers())); + thrown.expectCause(instanceOf(IllegalAccessException.class)); + thrown.expectMessage(both(stringContainsInOrder(Arrays.asList("cannot access a member"))) + .and(stringContainsInOrder(Arrays.asList("with modifiers \"private static final".split("", 1))))); + + privi.get(null); + } catch(Throwable t) { + throw new RuntimeException(t); + } + } + /** + * Test proves that the default enum constant is a class and specifically + * auto selected for Java 9. */ + @Test + public void testDefinedHelperPrivilegedField() { + try { + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + assertTrue(Modifier.isStatic(privi.getModifiers())); + Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + assertThat(inst, instanceOf(DefineClassHelper.class)); + privi.setAccessible(true); + Object p = privi.get(inst); + assertThat(""+p, equalTo("JAVA_9")); + assertThat(p.getClass().getName(), endsWith("SecuredPrivileged$1")); + } catch(Throwable t) { + throw new RuntimeException(t); + } + } + /** + * Test proves that caller class security is enforced and works + * as expected. */ + @Test + public void testDefinedHelperPrivilegedFieldMethodAccessDenied() { + try { + Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + privi.setAccessible(true); + Object priviInst = privi.get(inst); + Method defineClass = priviInst.getClass().getDeclaredMethod( + "defineClass", new Class[] { + String.class, byte[].class, int.class, int.class, + ClassLoader.class, ProtectionDomain.class + }); + + assertThat(defineClass, notNullValue()); + defineClass.setAccessible(true); + assertThat(defineClass.getName(), equalTo("defineClass")); + assertTrue(defineClass.canAccess(priviInst)); + ClassPool cp = ClassPool.getDefault(); + CtClass c = cp.makeClass("a.b.C"); + byte[] bc = c.toBytecode(); + + thrown.expectCause(instanceOf(IllegalAccessError.class)); + thrown.expectMessage(equalTo("java.lang.IllegalAccessError: Access denied for caller.")); + + @SuppressWarnings("unused") + Object res = defineClass.invoke(priviInst, new Object[] { + c.getName(), bc, 0, bc.length, new ClassLoader() {}, + ClassLoader.class.getProtectionDomain() + }); + } catch(InvocationTargetException t) { + throw new RuntimeException(t.getTargetException()); + } catch(Throwable t) { throw new RuntimeException(t); } + } + /** + * Test proves that we do have 3 enum constants in the private static + * inner class. */ + @Test + public void testDefinedHelperEnumClass() { + try { + Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + assertThat(DefineClassHelper.class.getDeclaredClasses(), arrayWithSize(1)); + Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; + assertTrue(secPriv.isEnum()); + assertThat(secPriv.getEnumConstants(), arrayWithSize(3)); + assertThat(""+secPriv.getEnumConstants()[0], equalTo("JAVA_9")); + assertThat(""+secPriv.getEnumConstants()[1], equalTo("JAVA_7")); + assertThat(""+secPriv.getEnumConstants()[2], equalTo("JAVA_OTHER")); + + } catch (Throwable t) {t.printStackTrace();} + + } + /** + * Test proves that you cannot modify private static final reference even + * with setAccessible(true). */ + @Test + public void testDefinedHelperCannotSetPrivileged() { + try { + Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; + Object J7 = secPriv.getEnumConstants()[1]; + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + privi.setAccessible(true); + thrown.expectCause(instanceOf(IllegalAccessException.class)); + thrown.expectMessage(startsWith("java.lang.IllegalAccessException: Can not set static final")); + privi.set(inst, J7); + + } catch (Throwable t) {throw new RuntimeException(t);} + + } + /** + * Test proves that you can achieve the impossible and modify private + * static final class reference without an instance. Now we can Mock + * test JDK 6 to 8 functionality */ + @Test + public void testDefinedHelperSetPrivilegedToJava7() { + try { + Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; + Object J9 = secPriv.getEnumConstants()[0]; + Object J7 = secPriv.getEnumConstants()[1]; + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + privi.setAccessible(true); + Object privInst = privi.get(inst); + Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe"); + unsf.setAccessible(true); + Object refu = unsf.get(privInst); + Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe"); + tuf.setAccessible(true); + Object tu = tuf.get(refu); + Method tu_call = tu.getClass().getMethod("call", new Class<?>[] {String.class, Object[].class}); + tu_call.setAccessible(true); + long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}}); + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J7}}); + + Object p = privi.get(inst); + assertThat(""+p, equalTo("JAVA_7")); + assertThat(p.getClass().getName(), endsWith("SecuredPrivileged$2")); + + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}}); + + } catch (Throwable t) {t.printStackTrace();} + + } + /** + * Test proves that Java 7+ MethodHandle defineClass (or DefineClassHelper.toClass) + * works as expected. */ + @Test + public void testDefinedHelperJava7ToClass() { + try { + Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; + Object J9 = secPriv.getEnumConstants()[0]; + Object J7 = secPriv.getEnumConstants()[1]; + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + privi.setAccessible(true); + Object privInst = privi.get(inst); + Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe"); + unsf.setAccessible(true); + Object refu = unsf.get(privInst); + Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe"); + tuf.setAccessible(true); + Object tu = tuf.get(refu); + Method tu_call = tu.getClass().getMethod("call", new Class<?>[] {String.class, Object[].class}); + tu_call.setAccessible(true); + long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}}); + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J7}}); + + ClassPool cp = ClassPool.getDefault(); + CtClass c = cp.makeClass("a.b.J7"); + byte[] bc = c.toBytecode(); + Class<?> bcCls = DefineClassHelper.toClass("a.b.J7", new ClassLoader() {}, null, bc); + assertThat(bcCls.getName(), equalTo("a.b.J7")); + assertThat(bcCls.getDeclaredConstructor().newInstance(), + not(equalTo(bcCls.getDeclaredConstructor().newInstance()))); + + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}}); + + } catch (Throwable t) {t.printStackTrace();} + + } + /** + * Test proves that Java 6 reflection method defineClass (or DefineClassHelper.toClass) + * works as expected. */ + @Test + public void testDefinedHelperJavaOtherToClass() { + try { + Constructor<DefineClassHelper> con = DefineClassHelper.class.getDeclaredConstructor(); + con.setAccessible(true); + DefineClassHelper inst = con.newInstance(); + Class<?> secPriv = DefineClassHelper.class.getDeclaredClasses()[0]; + Object J9 = secPriv.getEnumConstants()[0]; + Object JO = secPriv.getEnumConstants()[2]; + Field privi = DefineClassHelper.class.getDeclaredField("privileged"); + privi.setAccessible(true); + Object privInst = privi.get(inst); + Field unsf = privInst.getClass().getDeclaredField("sunMiscUnsafe"); + unsf.setAccessible(true); + Object refu = unsf.get(privInst); + Field tuf = refu.getClass().getDeclaredField("sunMiscUnsafeTheUnsafe"); + tuf.setAccessible(true); + Object tu = tuf.get(refu); + Method tu_call = tu.getClass().getMethod("call", new Class<?>[] {String.class, Object[].class}); + tu_call.setAccessible(true); + long offset = (Long) tu_call.invoke(tu, new Object[] {"staticFieldOffset", new Object[] {privi}}); + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, JO}}); + + ClassPool cp = ClassPool.getDefault(); + CtClass c = cp.makeClass("a.b.JO"); + byte[] bc = c.toBytecode(); + Class<?> bcCls = DefineClassHelper.toClass("a.b.JO", new ClassLoader() {}, null, bc); + assertThat(bcCls.getName(), equalTo("a.b.JO")); + assertThat(bcCls.getDeclaredConstructor().newInstance(), + not(equalTo(bcCls.getDeclaredConstructor().newInstance()))); + + tu_call.invoke(tu, new Object[] {"putObjectVolatile", new Object[] {DefineClassHelper.class, offset, J9}}); + + } catch (Throwable t) {t.printStackTrace();} + + } + /** + * Test proves that default Java 9 defineClass (or DefineClassHelper.toClass) + * works as expected. */ + @Test + public void testDefinedHelperDefaultToClass() { + try { + ClassPool cp = ClassPool.getDefault(); + CtClass c = cp.makeClass("a.b.D"); + byte[] bc = c.toBytecode(); + Class<?> bcCls = DefineClassHelper.toClass("a.b.D", new ClassLoader() {}, null, bc); + assertThat(bcCls.getName(), equalTo("a.b.D")); + assertThat(bcCls.getDeclaredConstructor().newInstance(), + not(equalTo(bcCls.getDeclaredConstructor().newInstance()))); + } catch (Throwable t) {t.printStackTrace();} + + } +} diff --git a/src/test/test1/FieldInit.java b/src/test/test1/FieldInit.java index 87f5a3bc..ee483149 100644 --- a/src/test/test1/FieldInit.java +++ b/src/test/test1/FieldInit.java @@ -14,4 +14,11 @@ public class FieldInit { --loop; } while (loop > 0); } + + public static class FI { + public FieldInit fi; + public FI(FieldInit fi) { + this.fi = fi; + } + } } diff --git a/src/test/test5/InnerModifier2.java b/src/test/test5/InnerModifier2.java new file mode 100644 index 00000000..009a0321 --- /dev/null +++ b/src/test/test5/InnerModifier2.java @@ -0,0 +1,13 @@ +package test5; + +@SuppressWarnings("unused") +public class InnerModifier2 { + public class Public {} + protected class Protected {} + private class Private {} + class Package {} + public static class PublicStatic {} + protected static class ProtectedStatic {} + private static class PrivateStatic {} + static class PackageStatic {} +} |