001/* 002 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package jdk.internal.jvmci.hotspot; 024 025import static java.lang.String.*; 026 027import java.io.*; 028import java.lang.reflect.*; 029import java.util.*; 030 031import jdk.internal.jvmci.common.*; 032import jdk.internal.org.objectweb.asm.*; 033import jdk.internal.org.objectweb.asm.Type; 034import sun.misc.*; 035 036/** 037 * A {@link ClassVisitor} that verifies {@link HotSpotVMConfig} does not access {@link Unsafe} from 038 * any of its non-static, non-constructor methods. This ensures that a deserialized 039 * {@link HotSpotVMConfig} object does not perform any unsafe reads on addresses that are only valid 040 * in the context in which the object was serialized. Note that this does not catch cases where a 041 * client uses an address stored in a {@link HotSpotVMConfig} field. 042 */ 043final class HotSpotVMConfigVerifier extends ClassVisitor { 044 045 public static boolean check() { 046 Class<?> cls = HotSpotVMConfig.class; 047 String classFilePath = "/" + cls.getName().replace('.', '/') + ".class"; 048 try { 049 InputStream classfile = cls.getResourceAsStream(classFilePath); 050 ClassReader cr = new ClassReader(Objects.requireNonNull(classfile, "Could not find class file for " + cls.getName())); 051 ClassVisitor cv = new HotSpotVMConfigVerifier(); 052 cr.accept(cv, 0); 053 return true; 054 } catch (IOException e) { 055 throw new JVMCIError(e); 056 } 057 } 058 059 /** 060 * Source file context for error reporting. 061 */ 062 String sourceFile = null; 063 064 /** 065 * Line number for error reporting. 066 */ 067 int lineNo = -1; 068 069 private static Class<?> resolve(String name) { 070 try { 071 return Class.forName(name.replace('/', '.')); 072 } catch (ClassNotFoundException e) { 073 throw new JVMCIError(e); 074 } 075 } 076 077 HotSpotVMConfigVerifier() { 078 super(Opcodes.ASM5); 079 } 080 081 @Override 082 public void visitSource(String source, String debug) { 083 this.sourceFile = source; 084 } 085 086 void verify(boolean condition, String message) { 087 if (!condition) { 088 error(message); 089 } 090 } 091 092 void error(String message) { 093 String errorMessage = format("%s:%d: %s is not allowed in the context of compilation replay. The unsafe access should be moved into the %s constructor and the result cached in a field", 094 sourceFile, lineNo, message, HotSpotVMConfig.class.getSimpleName()); 095 throw new JVMCIError(errorMessage); 096 097 } 098 099 @Override 100 public MethodVisitor visitMethod(int access, String name, String d, String signature, String[] exceptions) { 101 if (!Modifier.isStatic(access) && Modifier.isPublic(access) && !name.equals("<init>")) { 102 return new MethodVisitor(Opcodes.ASM5) { 103 104 @Override 105 public void visitLineNumber(int line, Label start) { 106 lineNo = line; 107 } 108 109 private Executable resolveMethod(String owner, String methodName, String methodDesc) { 110 Class<?> declaringClass = resolve(owner); 111 while (declaringClass != null) { 112 if (methodName.equals("<init>")) { 113 for (Constructor<?> c : declaringClass.getDeclaredConstructors()) { 114 if (methodDesc.equals(Type.getConstructorDescriptor(c))) { 115 return c; 116 } 117 } 118 } else { 119 Type[] argumentTypes = Type.getArgumentTypes(methodDesc); 120 for (Method m : declaringClass.getDeclaredMethods()) { 121 if (m.getName().equals(methodName)) { 122 if (Arrays.equals(argumentTypes, Type.getArgumentTypes(m))) { 123 if (Type.getReturnType(methodDesc).equals(Type.getReturnType(m))) { 124 return m; 125 } 126 } 127 } 128 } 129 } 130 declaringClass = declaringClass.getSuperclass(); 131 } 132 throw new NoSuchMethodError(owner + "." + methodName + methodDesc); 133 } 134 135 /** 136 * Checks whether a given method is allowed to be called. 137 */ 138 private boolean checkInvokeTarget(Executable method) { 139 if (method.getDeclaringClass().equals(Unsafe.class)) { 140 return false; 141 } 142 return true; 143 } 144 145 @Override 146 public void visitMethodInsn(int opcode, String owner, String methodName, String methodDesc, boolean itf) { 147 Executable callee = resolveMethod(owner, methodName, methodDesc); 148 verify(checkInvokeTarget(callee), "invocation of " + callee); 149 } 150 }; 151 } else { 152 return null; 153 } 154 } 155}