8229773: Resolve permissions for code source URLs lazily
authorredestad
Mon, 19 Aug 2019 06:13:52 +0200
changeset 57792 1b6806340400
parent 57791 34bbd91b1522
child 57793 d372747e8f08
8229773: Resolve permissions for code source URLs lazily Reviewed-by: alanb, mullan, rriggs, dfuchs
make/jdk/src/classes/build/tools/classlist/HelloClasslist.java
src/java.base/share/classes/java/lang/System.java
src/java.base/share/classes/java/security/CodeSource.java
src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
src/java.base/share/classes/sun/security/util/LazyCodeSourcePermissionCollection.java
--- a/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java	Sat Aug 17 06:20:49 2019 -0700
+++ b/make/jdk/src/classes/build/tools/classlist/HelloClasslist.java	Mon Aug 19 06:13:52 2019 +0200
@@ -32,6 +32,7 @@
 package build.tools.classlist;
 
 import java.net.InetAddress;
+import java.nio.file.FileSystems;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
@@ -56,6 +57,8 @@
 
     public static void main(String ... args) {
 
+        FileSystems.getDefault();
+
         List<String> strings = Arrays.asList("Hello", "World!", "From: ",
               InetAddress.getLoopbackAddress().toString());
 
--- a/src/java.base/share/classes/java/lang/System.java	Sat Aug 17 06:20:49 2019 -0700
+++ b/src/java.base/share/classes/java/lang/System.java	Mon Aug 19 06:13:52 2019 +0200
@@ -74,6 +74,7 @@
 import jdk.internal.logger.LocalizedLoggerWrapper;
 import jdk.internal.util.SystemProps;
 import jdk.internal.vm.annotation.Stable;
+import sun.nio.fs.DefaultFileSystemProvider;
 import sun.reflect.annotation.AnnotationType;
 import sun.nio.ch.Interruptible;
 import sun.security.util.SecurityConstants;
@@ -339,6 +340,8 @@
             if (security == null) {
                 // ensure image reader is initialized
                 Object.class.getResource("java/lang/ANY");
+                // ensure the default file system is initialized
+                DefaultFileSystemProvider.theFileSystem();
             }
             if (sm != null) {
                 try {
--- a/src/java.base/share/classes/java/security/CodeSource.java	Sat Aug 17 06:20:49 2019 -0700
+++ b/src/java.base/share/classes/java/security/CodeSource.java	Mon Aug 19 06:13:52 2019 +0200
@@ -57,7 +57,7 @@
      *
      * @serial
      */
-    private URL location;
+    private final URL location;
 
     /*
      * The code signers.
--- a/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Sat Aug 17 06:20:49 2019 -0700
+++ b/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Mon Aug 19 06:13:52 2019 +0200
@@ -25,8 +25,6 @@
 
 package jdk.internal.loader;
 
-import java.io.File;
-import java.io.FilePermission;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.module.ModuleDescriptor;
@@ -40,7 +38,6 @@
 import java.security.AccessController;
 import java.security.CodeSigner;
 import java.security.CodeSource;
-import java.security.Permission;
 import java.security.PermissionCollection;
 import java.security.PrivilegedAction;
 import java.security.PrivilegedActionException;
@@ -65,6 +62,7 @@
 import jdk.internal.module.ModulePatcher.PatchedModuleReader;
 import jdk.internal.module.Resources;
 import jdk.internal.vm.annotation.Stable;
+import sun.security.util.LazyCodeSourcePermissionCollection;
 
 
 /**
@@ -966,39 +964,9 @@
      */
     @Override
     protected PermissionCollection getPermissions(CodeSource cs) {
-        PermissionCollection perms = super.getPermissions(cs);
-
-        // add the permission to access the resource
-        URL url = cs.getLocation();
-        if (url == null)
-            return perms;
-
-        // avoid opening connection when URL is to resource in run-time image
-        if (url.getProtocol().equals("jrt")) {
-            perms.add(new RuntimePermission("accessSystemModules"));
-            return perms;
-        }
-
-        // open connection to determine the permission needed
-        try {
-            Permission p = url.openConnection().getPermission();
-            if (p != null) {
-                // for directories then need recursive access
-                if (p instanceof FilePermission) {
-                    String path = p.getName();
-                    if (path.endsWith(File.separator)) {
-                        path += "-";
-                        p = new FilePermission(path, "read");
-                    }
-                }
-                perms.add(p);
-            }
-        } catch (IOException ioe) { }
-
-        return perms;
+        return new LazyCodeSourcePermissionCollection(super.getPermissions(cs), cs);
     }
 
-
     // -- miscellaneous supporting methods
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/util/LazyCodeSourcePermissionCollection.java	Mon Aug 19 06:13:52 2019 +0200
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 sun.security.util;
+
+import java.io.File;
+import java.io.FilePermission;
+import java.io.IOException;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.util.Enumeration;
+
+/**
+ * This {@code PermissionCollection} implementation delegates to another
+ * {@code PermissionCollection}, taking care to lazily add the permission needed
+ * to read from the given {@code CodeSource} at first use, i.e., when either of
+ * {@link #elements}, {@link #implies} or {@link #toString} is called, or when
+ * the collection is serialized.
+ */
+public final class LazyCodeSourcePermissionCollection
+        extends PermissionCollection
+{
+    private static final long serialVersionUID = -6727011328946861783L;
+    private final PermissionCollection perms;
+    private final CodeSource cs;
+    private volatile boolean permissionAdded;
+
+    public LazyCodeSourcePermissionCollection(PermissionCollection perms,
+                                              CodeSource cs) {
+        this.perms = perms;
+        this.cs = cs;
+    }
+
+    private void ensureAdded() {
+        if (!permissionAdded) {
+            synchronized(perms) {
+                if (permissionAdded)
+                    return;
+
+                // open connection to determine the permission needed
+                URL location = cs.getLocation();
+                if (location != null) {
+                    try {
+                        Permission p = location.openConnection().getPermission();
+                        if (p != null) {
+                            // for directories then need recursive access
+                            if (p instanceof FilePermission) {
+                                String path = p.getName();
+                                if (path.endsWith(File.separator)) {
+                                    path += "-";
+                                    p = new FilePermission(path,
+                                            SecurityConstants.FILE_READ_ACTION);
+                                }
+                            }
+                            perms.add(p);
+                        }
+                    } catch (IOException ioe) {
+                    }
+                }
+                if (isReadOnly()) {
+                    perms.setReadOnly();
+                }
+                permissionAdded = true;
+            }
+        }
+    }
+
+    @Override
+    public void add(Permission permission) {
+        if (isReadOnly())
+            throw new SecurityException(
+                    "attempt to add a Permission to a readonly PermissionCollection");
+        perms.add(permission);
+    }
+
+    @Override
+    public boolean implies(Permission permission) {
+        ensureAdded();
+        return perms.implies(permission);
+    }
+
+    @Override
+    public Enumeration<Permission> elements() {
+        ensureAdded();
+        return perms.elements();
+    }
+
+    @Override
+    public String toString() {
+        ensureAdded();
+        return perms.toString();
+    }
+
+    /**
+     * On serialization, initialize and replace with the underlying
+     * permissions. This removes the laziness on deserialization.
+     */
+    private Object writeReplace() {
+        ensureAdded();
+        return perms;
+    }
+}