1 /*
2  * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 
25 package org.graalvm.compiler.core.test;
26 
27 import static java.nio.file.StandardOpenOption.READ;
28 import static java.nio.file.StandardOpenOption.WRITE;
29 
30 import java.io.File;
31 import java.io.IOException;
32 import java.nio.ByteBuffer;
33 import java.nio.MappedByteBuffer;
34 import java.nio.channels.FileChannel;
35 import java.nio.channels.FileChannel.MapMode;
36 import java.nio.file.Files;
37 import java.nio.file.Path;
38 
39 import org.graalvm.compiler.nodes.StructuredGraph;
40 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
41 import org.graalvm.compiler.phases.common.inlining.policy.InlineEverythingPolicy;
42 import org.graalvm.compiler.phases.tiers.HighTierContext;
43 import org.junit.Assert;
44 import org.junit.Assume;
45 import org.junit.Test;
46 
47 import jdk.vm.ci.code.InstalledCode;
48 import jdk.vm.ci.code.InvalidInstalledCodeException;
49 import jdk.vm.ci.meta.ResolvedJavaMethod;
50 import jdk.vm.ci.meta.ResolvedJavaType;
51 import sun.misc.Unsafe;
52 
53 public class MarkUnsafeAccessTest extends GraalCompilerTest {
54 
55     public static Unsafe unsafe;
56 
getRaw()57     public void getRaw() {
58         unsafe.getInt(0L);
59     }
60 
get()61     public void get() {
62         unsafe.getInt(null, 0L);
63     }
64 
putRaw()65     public void putRaw() {
66         unsafe.putInt(0L, 0);
67     }
68 
put()69     public void put() {
70         unsafe.putInt(null, 0L, 0);
71     }
72 
cas()73     public void cas() {
74         unsafe.compareAndSwapInt(null, 0, 0, 0);
75     }
76 
noAccess()77     public void noAccess() {
78         unsafe.addressSize();
79         unsafe.pageSize();
80     }
81 
assertHasUnsafe(String name, boolean hasUnsafe)82     private void assertHasUnsafe(String name, boolean hasUnsafe) {
83         Assert.assertEquals(hasUnsafe, compile(getResolvedJavaMethod(name), null).hasUnsafeAccess());
84     }
85 
86     @Test
testGet()87     public void testGet() {
88         assertHasUnsafe("get", true);
89         assertHasUnsafe("getRaw", true);
90     }
91 
92     @Test
testPut()93     public void testPut() {
94         assertHasUnsafe("put", true);
95         assertHasUnsafe("putRaw", true);
96     }
97 
98     @Test
testCas()99     public void testCas() {
100         assertHasUnsafe("cas", true);
101     }
102 
103     @Test
testNoAcces()104     public void testNoAcces() {
105         assertHasUnsafe("noAccess", false);
106     }
107 
108     @FunctionalInterface
109     private interface MappedByteBufferGetter {
get(MappedByteBuffer mbb)110         byte get(MappedByteBuffer mbb);
111     }
112 
113     @Test
testStandard()114     public void testStandard() throws IOException {
115         testMappedByteBuffer(MappedByteBuffer::get);
116     }
117 
118     @Test
testCompiled()119     public void testCompiled() throws IOException {
120         Assume.assumeFalse("Crashes on AArch64 (GR-8351)", System.getProperty("os.arch").equalsIgnoreCase("aarch64"));
121         ResolvedJavaMethod getMethod = asResolvedJavaMethod(getMethod(ByteBuffer.class, "get", new Class<?>[]{}));
122         ResolvedJavaType mbbClass = getMetaAccess().lookupJavaType(MappedByteBuffer.class);
123         ResolvedJavaMethod getMethodImpl = mbbClass.findUniqueConcreteMethod(getMethod).getResult();
124         Assert.assertNotNull(getMethodImpl);
125         StructuredGraph graph = parseForCompile(getMethodImpl);
126         HighTierContext highContext = getDefaultHighTierContext();
127         createCanonicalizerPhase().apply(graph, highContext);
128         new InliningPhase(new InlineEverythingPolicy(), createCanonicalizerPhase()).apply(graph, highContext);
129         InstalledCode compiledCode = getCode(getMethodImpl, graph);
130         testMappedByteBuffer(mbb -> {
131             try {
132                 return (byte) compiledCode.executeVarargs(mbb);
133             } catch (InvalidInstalledCodeException e) {
134                 Assert.fail();
135                 return 0;
136             }
137         });
138     }
139 
140     private static final int BLOCK_SIZE = 512;
141     private static final int BLOCK_COUNT = 16;
142 
testMappedByteBuffer(MappedByteBufferGetter getter)143     public void testMappedByteBuffer(MappedByteBufferGetter getter) throws IOException {
144         Path tmp = Files.createTempFile(null, null);
145         tmp.toFile().deleteOnExit();
146         FileChannel tmpFileChannel = FileChannel.open(tmp, READ, WRITE);
147         ByteBuffer bb = ByteBuffer.allocate(BLOCK_SIZE);
148         while (bb.remaining() >= 4) {
149             bb.putInt(0xA8A8A8A8);
150         }
151         for (int i = 0; i < BLOCK_COUNT; ++i) {
152             bb.flip();
153             while (bb.hasRemaining()) {
154                 tmpFileChannel.write(bb);
155             }
156         }
157         tmpFileChannel.force(true);
158         MappedByteBuffer mbb = tmpFileChannel.map(MapMode.READ_WRITE, 0, BLOCK_SIZE * BLOCK_COUNT);
159         Assert.assertEquals((byte) 0xA8, mbb.get());
160         mbb.position(mbb.position() + BLOCK_SIZE);
161         Assert.assertEquals((byte) 0xA8, mbb.get());
162         boolean truncated = false;
163         try {
164             tmpFileChannel.truncate(0);
165             tmpFileChannel.force(true);
166             truncated = true;
167         } catch (IOException e) {
168             // not all platforms support truncating memory-mapped files
169         }
170         Assume.assumeTrue(truncated);
171         try {
172             mbb.position(BLOCK_SIZE);
173             getter.get(mbb);
174 
175             // Make a call that goes into native code to materialize async exception
176             new File("").exists();
177         } catch (InternalError e) {
178             return;
179         }
180         Assert.fail("Expected exception");
181     }
182 }
183