You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DefineClassHelper.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. Alternatively, the contents of this file may be used under
  8. * the terms of the GNU Lesser General Public License Version 2.1 or later,
  9. * or the Apache License Version 2.0.
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. */
  16. package javassist.util.proxy;
  17. import java.lang.invoke.MethodHandle;
  18. import java.lang.invoke.MethodHandles;
  19. import java.lang.invoke.MethodHandles.Lookup;
  20. import java.lang.reflect.Method;
  21. import java.security.ProtectionDomain;
  22. import java.util.List;
  23. import javassist.CannotCompileException;
  24. import javassist.bytecode.ClassFile;
  25. /**
  26. * Helper class for invoking {@link ClassLoader#defineClass(String,byte[],int,int)}.
  27. *
  28. * @since 3.22
  29. */
  30. public class DefineClassHelper
  31. {
  32. private static enum SecuredPrivileged
  33. {
  34. JAVA_9 {
  35. final class ReferencedUnsafe
  36. {
  37. private final SecurityActions.TheUnsafe sunMiscUnsafeTheUnsafe;
  38. private final MethodHandle defineClass;
  39. ReferencedUnsafe(SecurityActions.TheUnsafe usf, MethodHandle meth)
  40. {
  41. this.sunMiscUnsafeTheUnsafe = usf;
  42. this.defineClass = meth;
  43. }
  44. Class<?> defineClass(String name, byte[] b, int off, int len,
  45. ClassLoader loader, ProtectionDomain protectionDomain)
  46. throws ClassFormatError {
  47. if (stack.getCallerClass() != SecuredPrivileged.JAVA_9.getClass())
  48. throw new IllegalAccessError("Access denied for caller.");
  49. try {
  50. return (Class<?>) defineClass.invokeWithArguments(
  51. sunMiscUnsafeTheUnsafe.theUnsafe,
  52. name, b, off, len, loader, protectionDomain);
  53. } catch (Throwable e) {
  54. if (e instanceof RuntimeException) throw (RuntimeException) e;
  55. if (e instanceof ClassFormatError) throw (ClassFormatError) e;
  56. throw new ClassFormatError(e.getMessage());
  57. }
  58. }
  59. }
  60. private final StackWalker stack = StackWalker
  61. .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
  62. private final ReferencedUnsafe sunMiscUnsafe = getReferencedUnsafe();
  63. private final ReferencedUnsafe getReferencedUnsafe()
  64. {
  65. if (null != SecuredPrivileged.JAVA_9
  66. && stack.getCallerClass() != this.getClass())
  67. throw new IllegalAccessError("Access denied for caller.");
  68. try {
  69. SecurityActions.TheUnsafe usf = SecurityActions.getSunMiscUnsafeAnonymously();
  70. List<Method> defineClassMethod = usf.methods.get("defineClass");
  71. // On Java 11+ the defineClass method does not exist anymore
  72. if (null == defineClassMethod)
  73. return null;
  74. MethodHandle meth = MethodHandles.lookup()
  75. .unreflect(defineClassMethod.get(0));
  76. return new ReferencedUnsafe(usf, meth);
  77. } catch (Throwable e) {
  78. throw new RuntimeException("cannot initialize", e);
  79. }
  80. }
  81. @Override
  82. public Class<?> defineClass(String name, byte[] b, int off, int len,
  83. ClassLoader loader, ProtectionDomain protectionDomain)
  84. throws ClassFormatError
  85. {
  86. if (stack.getCallerClass() != DefineClassHelper.class)
  87. throw new IllegalAccessError("Access denied for caller.");
  88. return sunMiscUnsafe.defineClass(name, b, off, len, loader,
  89. protectionDomain);
  90. }
  91. },
  92. JAVA_7 {
  93. private final SecurityActions stack = SecurityActions.stack;
  94. private final MethodHandle defineClass = getDefineClassMethodHandle();
  95. private final MethodHandle getDefineClassMethodHandle()
  96. {
  97. if (null != SecuredPrivileged.JAVA_7
  98. && stack.getCallerClass() != this.getClass())
  99. throw new IllegalAccessError("Access denied for caller.");
  100. try {
  101. return SecurityActions.getMethodHandle(ClassLoader.class,
  102. "defineClass", new Class[] {
  103. String.class, byte[].class, int.class, int.class,
  104. ProtectionDomain.class
  105. });
  106. } catch (NoSuchMethodException e) {
  107. throw new RuntimeException("cannot initialize", e);
  108. }
  109. }
  110. @Override
  111. protected Class<?> defineClass(String name, byte[] b, int off, int len,
  112. ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError
  113. {
  114. if (stack.getCallerClass() != DefineClassHelper.class)
  115. throw new IllegalAccessError("Access denied for caller.");
  116. try {
  117. return (Class<?>) defineClass.invokeWithArguments(
  118. loader, name, b, off, len, protectionDomain);
  119. } catch (Throwable e) {
  120. if (e instanceof RuntimeException) throw (RuntimeException) e;
  121. if (e instanceof ClassFormatError) throw (ClassFormatError) e;
  122. throw new ClassFormatError(e.getMessage());
  123. }
  124. }
  125. },
  126. JAVA_OTHER {
  127. private final Method defineClass = getDefineClassMethod();
  128. private final SecurityActions stack = SecurityActions.stack;
  129. private final Method getDefineClassMethod() {
  130. if (null != SecuredPrivileged.JAVA_OTHER
  131. && stack.getCallerClass() != this.getClass())
  132. throw new IllegalAccessError("Access denied for caller.");
  133. try {
  134. return SecurityActions.getDeclaredMethod(ClassLoader.class,
  135. "defineClass", new Class[] {
  136. String.class, byte[].class, int.class, int.class, ProtectionDomain.class
  137. });
  138. } catch (NoSuchMethodException e) {
  139. throw new RuntimeException("cannot initialize", e);
  140. }
  141. }
  142. @Override
  143. protected Class<?> defineClass(String name, byte[] b, int off, int len,
  144. ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError
  145. {
  146. if (stack.getCallerClass() != DefineClassHelper.class)
  147. throw new IllegalAccessError("Access denied for caller.");
  148. try {
  149. SecurityActions.setAccessible(defineClass, true);
  150. return (Class<?>) defineClass.invoke(loader, new Object[] {
  151. name, b, off, len, protectionDomain
  152. });
  153. } catch (Throwable e) {
  154. if (e instanceof ClassFormatError) throw (ClassFormatError) e;
  155. if (e instanceof RuntimeException) throw (RuntimeException) e;
  156. throw new ClassFormatError(e.getMessage());
  157. }
  158. finally {
  159. SecurityActions.setAccessible(defineClass, false);
  160. }
  161. }
  162. };
  163. protected abstract Class<?> defineClass(String name, byte[] b, int off, int len,
  164. ClassLoader loader, ProtectionDomain protectionDomain) throws ClassFormatError;
  165. }
  166. // Java 11+ removed sun.misc.Unsafe.defineClass, so we fallback to invoking defineClass on
  167. // ClassLoader until we have an implementation that uses MethodHandles.Lookup.defineClass
  168. private static final SecuredPrivileged privileged = ClassFile.MAJOR_VERSION > ClassFile.JAVA_10
  169. ? SecuredPrivileged.JAVA_OTHER
  170. : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_9
  171. ? SecuredPrivileged.JAVA_9
  172. : ClassFile.MAJOR_VERSION >= ClassFile.JAVA_7
  173. ? SecuredPrivileged.JAVA_7
  174. : SecuredPrivileged.JAVA_OTHER;
  175. /**
  176. * Loads a class file by a given class loader.
  177. *
  178. * <p>This first tries to use {@code sun.misc.Unsafe} to load a class.
  179. * Then it tries to use a {@code protected} method in {@code java.lang.ClassLoader}
  180. * via {@code PrivilegedAction}. Since the latter approach is not available
  181. * any longer by default in Java 9 or later, the JVM argument
  182. * {@code --add-opens java.base/java.lang=ALL-UNNAMED} must be given to the JVM.
  183. * If this JVM argument cannot be given, {@link #toPublicClass(String,byte[])}
  184. * should be used instead.
  185. * </p>
  186. *
  187. * @param domain if it is null, a default domain is used.
  188. * @since 3.22
  189. */
  190. public static Class<?> toClass(String className, ClassLoader loader,
  191. ProtectionDomain domain, byte[] bcode)
  192. throws CannotCompileException
  193. {
  194. try {
  195. return privileged.defineClass(className, bcode, 0, bcode.length, loader, domain);
  196. }
  197. catch (RuntimeException e) {
  198. throw e;
  199. }
  200. catch (ClassFormatError e) {
  201. throw new CannotCompileException(e.getCause());
  202. }
  203. catch (Exception e) {
  204. throw new CannotCompileException(e);
  205. }
  206. }
  207. /**
  208. * Loads a class file by {@code java.lang.invoke.MethodHandles.Lookup}.
  209. *
  210. * @since 3.22
  211. */
  212. static Class<?> toPublicClass(String className, byte[] bcode)
  213. throws CannotCompileException
  214. {
  215. try {
  216. Lookup lookup = MethodHandles.lookup();
  217. lookup = lookup.dropLookupMode(java.lang.invoke.MethodHandles.Lookup.PRIVATE);
  218. return lookup.defineClass(bcode);
  219. }
  220. catch (Throwable t) {
  221. throw new CannotCompileException(t);
  222. }
  223. }
  224. private DefineClassHelper() {}
  225. }