1 /*
2  * Copyright (c) 2015, 2018, 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.CanonicalizerPhase;
41 import org.graalvm.compiler.phases.common.inlining.InliningPhase;
42 import org.graalvm.compiler.phases.common.inlining.policy.InlineEverythingPolicy;
43 import org.graalvm.compiler.phases.tiers.HighTierContext;
44 import org.junit.Assert;
45 import org.junit.Assume;
46 import org.junit.Test;
47 
48 import jdk.vm.ci.code.InstalledCode;
49 import jdk.vm.ci.code.InvalidInstalledCodeException;
50 import jdk.vm.ci.meta.ResolvedJavaMethod;
51 import jdk.vm.ci.meta.ResolvedJavaType;
52 import sun.misc.Unsafe;
53 
54 public class MarkUnsafeAccessTest extends GraalCompilerTest {
55 
56     public static Unsafe unsafe;
57 
getRaw()58     public void getRaw() {
59         unsafe.getInt(0L);
60     }
61 
get()62     public void get() {
63         unsafe.getInt(null, 0L);
64     }
65 
putRaw()66     public void putRaw() {
67         unsafe.putInt(0L, 0);
68     }
69 
put()70     public void put() {
71         unsafe.putInt(null, 0L, 0);
72     }
73 
cas()74     public void cas() {
75         unsafe.compareAndSwapInt(null, 0, 0, 0);
76     }
77 
noAccess()78     public void noAccess() {
79         unsafe.addressSize();
80         unsafe.pageSize();
81     }
82 
assertHasUnsafe(String name, boolean hasUnsafe)83     private void assertHasUnsafe(String name, boolean hasUnsafe) {
84         Assert.assertEquals(hasUnsafe, compile(getResolvedJavaMethod(name), null).hasUnsafeAccess());
85     }
86 
87     @Test
testGet()88     public void testGet() {
89         assertHasUnsafe("get", true);
90         assertHasUnsafe("getRaw", true);
91     }
92 
93     @Test
testPut()94     public void testPut() {
95         assertHasUnsafe("put", true);
96         assertHasUnsafe("putRaw", true);
97     }
98 
99     @Test
testCas()100     public void testCas() {
101         assertHasUnsafe("cas", true);
102     }
103 
104     @Test
testNoAcces()105     public void testNoAcces() {
106         assertHasUnsafe("noAccess", false);
107     }
108 
109     @FunctionalInterface
110     private interface MappedByteBufferGetter {
get(MappedByteBuffer mbb)111         byte get(MappedByteBuffer mbb);
112     }
113 
114     @Test
testStandard()115     public void testStandard() throws IOException {
116         testMappedByteBuffer(MappedByteBuffer::get);
117     }
118 
119     @Test
testCompiled()120     public void testCompiled() throws IOException {
121         Assume.assumeFalse("Crashes on AArch64 (GR-8351)", System.getProperty("os.arch").equalsIgnoreCase("aarch64"));
122         ResolvedJavaMethod getMethod = asResolvedJavaMethod(getMethod(ByteBuffer.class, "get", new Class<?>[]{}));
123         ResolvedJavaType mbbClass = getMetaAccess().lookupJavaType(MappedByteBuffer.class);
124         ResolvedJavaMethod getMethodImpl = mbbClass.findUniqueConcreteMethod(getMethod).getResult();
125         Assert.assertNotNull(getMethodImpl);
126         StructuredGraph graph = parseForCompile(getMethodImpl);
127         HighTierContext highContext = getDefaultHighTierContext();
128         new CanonicalizerPhase().apply(graph, highContext);
129         new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase()).apply(graph, highContext);
130         InstalledCode compiledCode = getCode(getMethodImpl, graph);
131         testMappedByteBuffer(mbb -> {
132             try {
133                 return (byte) compiledCode.executeVarargs(mbb);
134             } catch (InvalidInstalledCodeException e) {
135                 Assert.fail();
136                 return 0;
137             }
138         });
139     }
140 
141     private static final int BLOCK_SIZE = 512;
142     private static final int BLOCK_COUNT = 16;
143 
testMappedByteBuffer(MappedByteBufferGetter getter)144     public void testMappedByteBuffer(MappedByteBufferGetter getter) throws IOException {
145         Path tmp = Files.createTempFile(null, null);
146         tmp.toFile().deleteOnExit();
147         FileChannel tmpFileChannel = FileChannel.open(tmp, READ, WRITE);
148         ByteBuffer bb = ByteBuffer.allocate(BLOCK_SIZE);
149         while (bb.remaining() >= 4) {
150             bb.putInt(0xA8A8A8A8);
151         }
152         for (int i = 0; i < BLOCK_COUNT; ++i) {
153             bb.flip();
154             while (bb.hasRemaining()) {
155                 tmpFileChannel.write(bb);
156             }
157         }
158         tmpFileChannel.force(true);
159         MappedByteBuffer mbb = tmpFileChannel.map(MapMode.READ_WRITE, 0, BLOCK_SIZE * BLOCK_COUNT);
160         Assert.assertEquals((byte) 0xA8, mbb.get());
161         mbb.position(mbb.position() + BLOCK_SIZE);
162         Assert.assertEquals((byte) 0xA8, mbb.get());
163         boolean truncated = false;
164         try {
165             tmpFileChannel.truncate(0);
166             tmpFileChannel.force(true);
167             truncated = true;
168         } catch (IOException e) {
169             // not all platforms support truncating memory-mapped files
170         }
171         Assume.assumeTrue(truncated);
172         try {
173             mbb.position(BLOCK_SIZE);
174             getter.get(mbb);
175 
176             // Make a call that goes into native code to materialize async exception
177             new File("").exists();
178         } catch (InternalError e) {
179             return;
180         }
181         Assert.fail("Expected exception");
182     }
183 }
184