1 /*
   2  * Copyright (c) 2014, 2014, 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 package org.graalvm.compiler.jtt.except;
  24 
  25 import org.junit.BeforeClass;
  26 import org.junit.Test;
  27 
  28 import org.graalvm.compiler.jtt.JTTTest;
  29 import org.graalvm.compiler.test.ExportingClassLoader;
  30 
  31 import jdk.internal.org.objectweb.asm.ClassWriter;
  32 import jdk.internal.org.objectweb.asm.MethodVisitor;
  33 import jdk.internal.org.objectweb.asm.Opcodes;
  34 import jdk.internal.org.objectweb.asm.Type;
  35 
  36 public class UntrustedInterfaces extends JTTTest {
  37 
  38     public interface CallBack {
  39         int callBack(TestInterface ti);
  40     }
  41 
  42     private interface TestInterface {
  43         int method();
  44     }
  45 
  46     /**
  47      * What a GoodPill would look like.
  48      *
  49      * <pre>
  50      * private static final class GoodPill extends Pill {
  51      *     public void setField() {
  52      *         field = new TestConstant();
  53      *     }
  54      *
  55      *     public void setStaticField() {
  56      *         staticField = new TestConstant();
  57      *     }
  58      *
  59      *     public int callMe(CallBack callback) {
  60      *         return callback.callBack(new TestConstant());
  61      *     }
  62      *
  63      *     public TestInterface get() {
  64      *         return new TestConstant();
  65      *     }
  66      * }
  67      *
  68      * private static final class TestConstant implements TestInterface {
  69      *     public int method() {
  70      *         return 42;
  71      *     }
  72      * }
  73      * </pre>
  74      */
  75     public abstract static class Pill {
  76         public static TestInterface staticField;
  77         public TestInterface field;
  78 
  79         public abstract void setField();
  80 
  81         public abstract void setStaticField();
  82 
  83         public abstract int callMe(CallBack callback);
  84 
  85         public abstract TestInterface get();
  86     }
  87 
  88     public int callBack(TestInterface list) {
  89         return list.method();
  90     }
  91 
  92     public int staticFieldInvoke(Pill pill) {
  93         pill.setStaticField();
  94         return Pill.staticField.method();
  95     }
  96 
  97     public int fieldInvoke(Pill pill) {
  98         pill.setField();
  99         return pill.field.method();
 100     }
 101 
 102     public int argumentInvoke(Pill pill) {
 103         return pill.callMe(ti -> ti.method());
 104     }
 105 
 106     public int returnInvoke(Pill pill) {
 107         return pill.get().method();
 108     }
 109 
 110     @SuppressWarnings("cast")
 111     public boolean staticFieldInstanceof(Pill pill) {
 112         pill.setStaticField();
 113         return Pill.staticField instanceof TestInterface;
 114     }
 115 
 116     @SuppressWarnings("cast")
 117     public boolean fieldInstanceof(Pill pill) {
 118         pill.setField();
 119         return pill.field instanceof TestInterface;
 120     }
 121 
 122     @SuppressWarnings("cast")
 123     public int argumentInstanceof(Pill pill) {
 124         return pill.callMe(ti -> ti instanceof TestInterface ? 42 : 24);
 125     }
 126 
 127     @SuppressWarnings("cast")
 128     public boolean returnInstanceof(Pill pill) {
 129         return pill.get() instanceof TestInterface;
 130     }
 131 
 132     public TestInterface staticFieldCheckcast(Pill pill) {
 133         pill.setStaticField();
 134         return TestInterface.class.cast(Pill.staticField);
 135     }
 136 
 137     public TestInterface fieldCheckcast(Pill pill) {
 138         pill.setField();
 139         return TestInterface.class.cast(pill.field);
 140     }
 141 
 142     public int argumentCheckcast(Pill pill) {
 143         return pill.callMe(ti -> TestInterface.class.cast(ti).method());
 144     }
 145 
 146     public TestInterface returnCheckcast(Pill pill) {
 147         return TestInterface.class.cast(pill.get());
 148     }
 149 
 150     private static Pill poisonPill;
 151 
 152     // Checkstyle: stop
 153     @BeforeClass
 154     public static void setUp() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
 155         poisonPill = (Pill) new PoisonLoader().findClass(PoisonLoader.POISON_IMPL_NAME).newInstance();
 156     }
 157 
 158     // Checkstyle: resume
 159 
 160     @Test
 161     public void testStaticField0() {
 162         runTest("staticFieldInvoke", poisonPill);
 163     }
 164 
 165     @Test
 166     public void testStaticField1() {
 167         runTest("staticFieldInstanceof", poisonPill);
 168     }
 169 
 170     @Test
 171     public void testStaticField2() {
 172         runTest("staticFieldCheckcast", poisonPill);
 173     }
 174 
 175     @Test
 176     public void testField0() {
 177         runTest("fieldInvoke", poisonPill);
 178     }
 179 
 180     @Test
 181     public void testField1() {
 182         runTest("fieldInstanceof", poisonPill);
 183     }
 184 
 185     @Test
 186     public void testField2() {
 187         runTest("fieldCheckcast", poisonPill);
 188     }
 189 
 190     @Test
 191     public void testArgument0() {
 192         runTest("argumentInvoke", poisonPill);
 193     }
 194 
 195     @Test
 196     public void testArgument1() {
 197         runTest("argumentInstanceof", poisonPill);
 198     }
 199 
 200     @Test
 201     public void testArgument2() {
 202         runTest("argumentCheckcast", poisonPill);
 203     }
 204 
 205     @Test
 206     public void testReturn0() {
 207         runTest("returnInvoke", poisonPill);
 208     }
 209 
 210     @Test
 211     public void testReturn1() {
 212         runTest("returnInstanceof", poisonPill);
 213     }
 214 
 215     @Test
 216     public void testReturn2() {
 217         runTest("returnCheckcast", poisonPill);
 218     }
 219 
 220     private static class PoisonLoader extends ExportingClassLoader {
 221         public static final String POISON_IMPL_NAME = "org.graalvm.compiler.jtt.except.PoisonPill";
 222 
 223         @Override
 224         protected Class<?> findClass(String name) throws ClassNotFoundException {
 225             if (name.equals(POISON_IMPL_NAME)) {
 226                 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
 227 
 228                 cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, POISON_IMPL_NAME.replace('.', '/'), null, Type.getInternalName(Pill.class), null);
 229                 // constructor
 230                 MethodVisitor constructor = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
 231                 constructor.visitCode();
 232                 constructor.visitVarInsn(Opcodes.ALOAD, 0);
 233                 constructor.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Pill.class), "<init>", "()V", false);
 234                 constructor.visitInsn(Opcodes.RETURN);
 235                 constructor.visitMaxs(0, 0);
 236                 constructor.visitEnd();
 237 
 238                 MethodVisitor setList = cw.visitMethod(Opcodes.ACC_PUBLIC, "setField", "()V", null, null);
 239                 setList.visitCode();
 240                 setList.visitVarInsn(Opcodes.ALOAD, 0);
 241                 setList.visitTypeInsn(Opcodes.NEW, Type.getInternalName(Object.class));
 242                 setList.visitInsn(Opcodes.DUP);
 243                 setList.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V", false);
 244                 setList.visitFieldInsn(Opcodes.PUTFIELD, Type.getInternalName(Pill.class), "field", Type.getDescriptor(TestInterface.class));
 245                 setList.visitInsn(Opcodes.RETURN);
 246                 setList.visitMaxs(0, 0);
 247                 setList.visitEnd();
 248 
 249                 MethodVisitor setStaticList = cw.visitMethod(Opcodes.ACC_PUBLIC, "setStaticField", "()V", null, null);
 250                 setStaticList.visitCode();
 251                 setStaticList.visitTypeInsn(Opcodes.NEW, Type.getInternalName(Object.class));
 252                 setStaticList.visitInsn(Opcodes.DUP);
 253                 setStaticList.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V", false);
 254                 setStaticList.visitFieldInsn(Opcodes.PUTSTATIC, Type.getInternalName(Pill.class), "staticField", Type.getDescriptor(TestInterface.class));
 255                 setStaticList.visitInsn(Opcodes.RETURN);
 256                 setStaticList.visitMaxs(0, 0);
 257                 setStaticList.visitEnd();
 258 
 259                 MethodVisitor callMe = cw.visitMethod(Opcodes.ACC_PUBLIC, "callMe", Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(CallBack.class)), null, null);
 260                 callMe.visitCode();
 261                 callMe.visitVarInsn(Opcodes.ALOAD, 1);
 262                 callMe.visitTypeInsn(Opcodes.NEW, Type.getInternalName(Object.class));
 263                 callMe.visitInsn(Opcodes.DUP);
 264                 callMe.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V", false);
 265                 callMe.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(CallBack.class), "callBack", Type.getMethodDescriptor(Type.INT_TYPE, Type.getType(TestInterface.class)), true);
 266                 callMe.visitInsn(Opcodes.IRETURN);
 267                 callMe.visitMaxs(0, 0);
 268                 callMe.visitEnd();
 269 
 270                 MethodVisitor getList = cw.visitMethod(Opcodes.ACC_PUBLIC, "get", Type.getMethodDescriptor(Type.getType(TestInterface.class)), null, null);
 271                 getList.visitCode();
 272                 getList.visitTypeInsn(Opcodes.NEW, Type.getInternalName(Object.class));
 273                 getList.visitInsn(Opcodes.DUP);
 274                 getList.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V", false);
 275                 getList.visitInsn(Opcodes.ARETURN);
 276                 getList.visitMaxs(0, 0);
 277                 getList.visitEnd();
 278 
 279                 cw.visitEnd();
 280 
 281                 byte[] bytes = cw.toByteArray();
 282                 return defineClass(name, bytes, 0, bytes.length);
 283             }
 284             return super.findClass(name);
 285         }
 286     }
 287 }