changeset 18652:dfacef5b7958

remove uses of Unsafe in HotSpotVMConfig methods that may be executed on a deserialized HotSpotVMConfig object
author Doug Simon <doug.simon@oracle.com>
date Tue, 09 Dec 2014 22:04:06 +0100
parents 8c753e6fab96
children 8bdc3428cf0e
files graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfigVerifier.java
diffstat 2 files changed, 196 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Tue Dec 09 21:58:44 2014 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Tue Dec 09 22:04:06 2014 +0100
@@ -74,7 +74,29 @@
         oopEncoding = new CompressEncoding(narrowOopBase, narrowOopShift, logMinObjAlignment());
         klassEncoding = new CompressEncoding(narrowKlassBase, narrowKlassShift, logKlassAlignment);
 
+        codeCacheLowBoundary = unsafe.getAddress(codeCacheHeap + codeHeapMemoryOffset + virtualSpaceLowBoundaryOffset);
+        codeCacheHighBoundary = unsafe.getAddress(codeCacheHeap + codeHeapMemoryOffset + virtualSpaceHighBoundaryOffset);
+
+        final long barrierSetAddress = unsafe.getAddress(universeCollectedHeap + collectedHeapBarrierSetOffset);
+        final int kind = unsafe.getInt(barrierSetAddress + barrierSetKindOffset);
+        if ((kind == barrierSetCardTableModRef) || (kind == barrierSetCardTableExtension) || (kind == barrierSetG1SATBCT) || (kind == barrierSetG1SATBCTLogging)) {
+            final long base = unsafe.getAddress(barrierSetAddress + cardTableModRefBSByteMapBaseOffset);
+            assert base != 0 : "unexpected byte_map_base: " + base;
+            cardtableStartAddress = base;
+            cardtableShift = cardTableModRefBSCardShift;
+        } else if ((kind == barrierSetModRef) || (kind == barrierSetOther)) {
+            // No post barriers
+            cardtableStartAddress = 0;
+            cardtableShift = 0;
+        } else {
+            cardtableStartAddress = -1;
+            cardtableShift = -1;
+        }
+
+        inlineCacheMissStub = inlineCacheMissBlob + unsafe.getInt(inlineCacheMissBlob + codeBlobCodeOffsetOffset);
+
         assert check();
+        assert HotSpotVMConfigVerifier.check();
     }
 
     /**
@@ -1205,32 +1227,21 @@
     @HotSpotVMValue(expression = "(jbyte)CardTableModRefBS::dirty_card_val()") @Stable public byte dirtyCardValue;
     @HotSpotVMValue(expression = "(jbyte)G1SATBCardTableModRefBS::g1_young_card_val()") @Stable public byte g1YoungCardValue;
 
+    private final long cardtableStartAddress;
+    private final int cardtableShift;
+
     public long cardtableStartAddress() {
-        final long barrierSetAddress = unsafe.getAddress(universeCollectedHeap + collectedHeapBarrierSetOffset);
-        final int kind = unsafe.getInt(barrierSetAddress + barrierSetKindOffset);
-        if ((kind == barrierSetCardTableModRef) || (kind == barrierSetCardTableExtension) || (kind == barrierSetG1SATBCT) || (kind == barrierSetG1SATBCTLogging)) {
-            final long base = unsafe.getAddress(barrierSetAddress + cardTableModRefBSByteMapBaseOffset);
-            assert base != 0 : "unexpected byte_map_base: " + base;
-            return base;
+        if (cardtableStartAddress == -1) {
+            throw GraalInternalError.shouldNotReachHere();
         }
-        if ((kind == barrierSetModRef) || (kind == barrierSetOther)) {
-            // No post barriers
-            return 0;
-        }
-        throw GraalInternalError.shouldNotReachHere("kind: " + kind);
+        return cardtableStartAddress;
     }
 
     public int cardtableShift() {
-        final long barrierSetAddress = unsafe.getAddress(universeCollectedHeap + collectedHeapBarrierSetOffset);
-        final int kind = unsafe.getInt(barrierSetAddress + barrierSetKindOffset);
-        if ((kind == barrierSetCardTableModRef) || (kind == barrierSetCardTableExtension) || (kind == barrierSetG1SATBCT) || (kind == barrierSetG1SATBCTLogging)) {
-            return cardTableModRefBSCardShift;
+        if (cardtableShift == -1) {
+            throw GraalInternalError.shouldNotReachHere();
         }
-        if ((kind == barrierSetModRef) || (kind == barrierSetOther)) {
-            // No post barriers
-            return 0;
-        }
-        throw GraalInternalError.shouldNotReachHere("kind: " + kind);
+        return cardtableShift;
     }
 
     @HotSpotVMField(name = "os::_polling_page", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long safepointPollingAddress;
@@ -1367,8 +1378,10 @@
     @HotSpotVMField(name = "CodeBlob::_code_offset", type = "int", get = HotSpotVMField.Type.OFFSET) @Stable private int codeBlobCodeOffsetOffset;
     @HotSpotVMField(name = "SharedRuntime::_ic_miss_blob", type = "RuntimeStub*", get = HotSpotVMField.Type.VALUE) @Stable private long inlineCacheMissBlob;
 
+    private final long inlineCacheMissStub;
+
     public long inlineCacheMissStub() {
-        return inlineCacheMissBlob + unsafe.getInt(inlineCacheMissBlob + codeBlobCodeOffsetOffset);
+        return inlineCacheMissStub;
     }
 
     @HotSpotVMField(name = "CodeCache::_heap", type = "CodeHeap*", get = HotSpotVMField.Type.VALUE) @Stable private long codeCacheHeap;
@@ -1376,18 +1389,21 @@
     @HotSpotVMField(name = "VirtualSpace::_low_boundary", type = "char*", get = HotSpotVMField.Type.OFFSET) @Stable private int virtualSpaceLowBoundaryOffset;
     @HotSpotVMField(name = "VirtualSpace::_high_boundary", type = "char*", get = HotSpotVMField.Type.OFFSET) @Stable private int virtualSpaceHighBoundaryOffset;
 
+    private final long codeCacheLowBoundary;
+    private final long codeCacheHighBoundary;
+
     /**
      * @return CodeCache::_heap-&gt;_memory._low_boundary
      */
     public long codeCacheLowBoundary() {
-        return unsafe.getAddress(codeCacheHeap + codeHeapMemoryOffset + virtualSpaceLowBoundaryOffset);
+        return codeCacheLowBoundary;
     }
 
     /**
      * @return CodeCache::_heap-&gt;_memory._high_boundary
      */
     public long codeCacheHighBoundary() {
-        return unsafe.getAddress(codeCacheHeap + codeHeapMemoryOffset + virtualSpaceHighBoundaryOffset);
+        return codeCacheHighBoundary;
     }
 
     @HotSpotVMField(name = "StubRoutines::_aescrypt_encryptBlock", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long aescryptEncryptBlockStub;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfigVerifier.java	Tue Dec 09 22:04:06 2014 +0100
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.hotspot;
+
+import static java.lang.String.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.oracle.graal.compiler.common.*;
+
+import jdk.internal.org.objectweb.asm.*;
+import jdk.internal.org.objectweb.asm.Type;
+import sun.misc.*;
+
+/**
+ * A {@link ClassVisitor} that verifies {@link HotSpotVMConfig} does not access {@link Unsafe} from
+ * any of its non-static, non-constructor methods. This ensures that a deserialized
+ * {@link HotSpotVMConfig} object does not perform any unsafe reads on addresses that are only valid
+ * in the context in which the object was serialized. Note that this does not catch cases where a
+ * client uses an address stored in a {@link HotSpotVMConfig} field.
+ */
+final class HotSpotVMConfigVerifier extends ClassVisitor {
+
+    public static boolean check() {
+        Class<?> cls = HotSpotVMConfig.class;
+        String classFilePath = "/" + cls.getName().replace('.', '/') + ".class";
+        try {
+            InputStream classfile = cls.getResourceAsStream(classFilePath);
+            ClassReader cr = new ClassReader(Objects.requireNonNull(classfile, "Could not find class file for " + cls.getName()));
+            ClassVisitor cv = new HotSpotVMConfigVerifier();
+            cr.accept(cv, 0);
+            return true;
+        } catch (IOException e) {
+            throw new GraalInternalError(e);
+        }
+    }
+
+    /**
+     * Source file context for error reporting.
+     */
+    String sourceFile = null;
+
+    /**
+     * Line number for error reporting.
+     */
+    int lineNo = -1;
+
+    private static Class<?> resolve(String name) {
+        try {
+            return Class.forName(name.replace('/', '.'));
+        } catch (ClassNotFoundException e) {
+            throw new InternalError(e);
+        }
+    }
+
+    HotSpotVMConfigVerifier() {
+        super(Opcodes.ASM5);
+    }
+
+    @Override
+    public void visitSource(String source, String debug) {
+        this.sourceFile = source;
+    }
+
+    void verify(boolean condition, String name, String signature, String message) {
+        if (!condition) {
+            error(message, name, signature);
+        }
+    }
+
+    void error(String message, String name, String signature) {
+        String errorMessage = format(
+                        "%s:%d: Use of Unsafe in %s.%s%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 that will be returned by %s%s%n",
+                        sourceFile, lineNo, HotSpotVMConfig.class.getSimpleName(), name, signature, message, HotSpotVMConfig.class.getSimpleName(), name, signature);
+        throw new InternalError(errorMessage);
+
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String d, String signature, String[] exceptions) {
+        if (!Modifier.isStatic(access) && Modifier.isPublic(access) && !name.equals("<init>")) {
+            return new MethodVisitor(Opcodes.ASM5) {
+
+                @Override
+                public void visitLineNumber(int line, Label start) {
+                    lineNo = line;
+                }
+
+                private Executable resolveMethod(String owner, String methodName, String methodDesc) {
+                    Class<?> declaringClass = resolve(owner);
+                    while (declaringClass != null) {
+                        if (methodName.equals("<init>")) {
+                            for (Constructor<?> c : declaringClass.getDeclaredConstructors()) {
+                                if (methodDesc.equals(Type.getConstructorDescriptor(c))) {
+                                    return c;
+                                }
+                            }
+                        } else {
+                            Type[] argumentTypes = Type.getArgumentTypes(methodDesc);
+                            for (Method m : declaringClass.getDeclaredMethods()) {
+                                if (m.getName().equals(methodName)) {
+                                    if (Arrays.equals(argumentTypes, Type.getArgumentTypes(m))) {
+                                        if (Type.getReturnType(methodDesc).equals(Type.getReturnType(m))) {
+                                            return m;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        declaringClass = declaringClass.getSuperclass();
+                    }
+                    throw new NoSuchMethodError(owner + "." + methodName + methodDesc);
+                }
+
+                /**
+                 * Checks whether a given method is allowed to be called.
+                 */
+                private boolean checkInvokeTarget(Executable method) {
+                    if (method.getDeclaringClass().equals(Unsafe.class)) {
+                        return false;
+                    }
+                    return true;
+                }
+
+                @Override
+                public void visitMethodInsn(int opcode, String owner, String methodName, String methodDesc, boolean itf) {
+                    Executable callee = resolveMethod(owner, methodName, methodDesc);
+                    verify(checkInvokeTarget(callee), name, d, "invocation of " + callee);
+                }
+            };
+        } else {
+            return null;
+        }
+    }
+}