1 /* 2 * Copyright (c) 2019, 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 * @test 26 * @summary Verifies that privileged operations performed in the record 27 * constructor throw, when run without the required permissions 28 * @compile --enable-preview -source ${jdk.version} ConstructorPermissionTest.java 29 * @run testng/othervm/java.security.policy=empty_security.policy --enable-preview ConstructorPermissionTest 30 */ 31 32 import java.io.ByteArrayInputStream; 33 import java.io.ByteArrayOutputStream; 34 import java.io.IOException; 35 import java.io.InvalidObjectException; 36 import java.io.ObjectInputStream; 37 import java.io.ObjectOutputStream; 38 import java.io.Serializable; 39 import java.net.Socket; 40 import java.nio.file.Files; 41 import java.nio.file.Path; 42 import java.security.AccessControlException; 43 import org.testng.annotations.DataProvider; 44 import org.testng.annotations.Test; 45 import static java.lang.System.out; 46 import static org.testng.Assert.assertTrue; 47 import static org.testng.Assert.expectThrows; 48 49 /** 50 * Ensures that the appropriate exception, invalid object exception, with a 51 * suitable cause, is thrown when the record constructor performs a privileged 52 * operation without permission. 53 */ 54 public class ConstructorPermissionTest { 55 56 /** "big switch" that can be used to allow/disallow record construction 57 * set to true after the data provider has constructed all record objects */ 58 private static volatile boolean firstDataSetCreated; 59 60 record R1 () implements Serializable { 61 public R1 { 62 if (firstDataSetCreated) { 63 try { Files.list(Path.of(".")); } 64 catch (IOException unexpected) { throw new AssertionError(unexpected); } 65 } 66 } 67 } 68 69 record R2 (int x) implements Serializable { 70 public R2 { 71 if (firstDataSetCreated) { 72 try { new Socket("localhost", 8080); } 73 catch (IOException unexpected) { throw new AssertionError(unexpected); } 74 } 75 } 76 } 77 78 record R3 (String... args) implements Serializable { 79 public R3 { 80 if (firstDataSetCreated) ProcessHandle.current()81 ProcessHandle.current(); 82 } 83 } 84 85 static final Class<InvalidObjectException> IOE = InvalidObjectException.class; 86 87 @DataProvider(name = "exceptionInstances") exceptionInstances()88 public Object[][] exceptionInstances() { 89 var objs = new Object[][] { 90 new Object[] { new R1(), AccessControlException.class, "FilePermission" }, 91 new Object[] { new R2(1), AccessControlException.class, "SocketPermission" }, 92 new Object[] { new R3("s"), AccessControlException.class, "manageProcess" }, 93 }; 94 firstDataSetCreated = true; 95 return objs; 96 } 97 98 @Test(dataProvider = "exceptionInstances") testExceptions(Object objectToSerialize, Class<? extends Throwable> expectedExType, String expectedExMessage)99 public void testExceptions(Object objectToSerialize, 100 Class<? extends Throwable> expectedExType, 101 String expectedExMessage) 102 throws Exception 103 { 104 out.println("\n---"); 105 out.println("serializing: " + objectToSerialize); 106 byte[] bytes = serialize(objectToSerialize); 107 InvalidObjectException ioe = expectThrows(IOE, () -> deserialize(bytes)); 108 out.println("caught expected IOE: " + ioe); 109 Throwable t = ioe.getCause(); 110 assertTrue(t.getClass().equals(expectedExType), 111 "Expected:" + expectedExType + ", got:" + t); 112 out.println("expected cause " + expectedExType +" : " + t); 113 String msg = t.getMessage(); 114 assertTrue(msg.contains(expectedExMessage), 115 "Expected message to contain [" + expectedExMessage + "], in " + msg); 116 } 117 118 // TODO: add positive tests with permissions granted. 119 120 // --- infra 121 serialize(T obj)122 static <T> byte[] serialize(T obj) throws IOException { 123 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 124 ObjectOutputStream oos = new ObjectOutputStream(baos); 125 oos.writeObject(obj); 126 oos.close(); 127 return baos.toByteArray(); 128 } 129 130 @SuppressWarnings("unchecked") deserialize(byte[] streamBytes)131 static <T> T deserialize(byte[] streamBytes) 132 throws IOException, ClassNotFoundException 133 { 134 ByteArrayInputStream bais = new ByteArrayInputStream(streamBytes); 135 ObjectInputStream ois = new ObjectInputStream(bais); 136 return (T) ois.readObject(); 137 } 138 } 139