changeset 60100:b97c78f3f588

8243592: Subject$SecureSet::addAll should not call contains(null) Reviewed-by: mullan
author weijun
date Thu, 09 Jul 2020 09:22:01 +0800
parents 8d1e1baf9600
children f1acad5b334f
files src/java.base/share/classes/javax/security/auth/Subject.java test/jdk/javax/security/auth/Subject/UnreliableContains.java
diffstat 2 files changed, 99 insertions(+), 42 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/javax/security/auth/Subject.java	Wed Jul 08 15:46:02 2020 -0700
+++ b/src/java.base/share/classes/javax/security/auth/Subject.java	Thu Jul 09 09:22:01 2020 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2020, 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
@@ -203,18 +203,20 @@
      *          Sets.
      */
     public Subject(boolean readOnly, Set<? extends Principal> principals,
-                   Set<?> pubCredentials, Set<?> privCredentials)
-    {
-        collectionNullClean(principals);
-        collectionNullClean(pubCredentials);
-        collectionNullClean(privCredentials);
+                   Set<?> pubCredentials, Set<?> privCredentials) {
+        LinkedList<Principal> principalList
+                = collectionNullClean(principals);
+        LinkedList<Object> pubCredsList
+                = collectionNullClean(pubCredentials);
+        LinkedList<Object> privCredsList
+                = collectionNullClean(privCredentials);
 
-        this.principals = Collections.synchronizedSet(new SecureSet<>
-                                (this, PRINCIPAL_SET, principals));
-        this.pubCredentials = Collections.synchronizedSet(new SecureSet<>
-                                (this, PUB_CREDENTIAL_SET, pubCredentials));
-        this.privCredentials = Collections.synchronizedSet(new SecureSet<>
-                                (this, PRIV_CREDENTIAL_SET, privCredentials));
+        this.principals = Collections.synchronizedSet(
+                new SecureSet<>(this, PRINCIPAL_SET, principalList));
+        this.pubCredentials = Collections.synchronizedSet(
+                new SecureSet<>(this, PUB_CREDENTIAL_SET, pubCredsList));
+        this.privCredentials = Collections.synchronizedSet(
+                new SecureSet<>(this, PRIV_CREDENTIAL_SET, privCredsList));
         this.readOnly = readOnly;
     }
 
@@ -975,8 +977,9 @@
 
         // Rewrap the principals into a SecureSet
         try {
+            LinkedList<Principal> principalList = collectionNullClean(inputPrincs);
             principals = Collections.synchronizedSet(new SecureSet<>
-                                (this, PRINCIPAL_SET, inputPrincs));
+                                (this, PRINCIPAL_SET, principalList));
         } catch (NullPointerException npe) {
             // Sometimes people deserialize the principals set only.
             // Subject is not accessible, so just don't fail.
@@ -1001,26 +1004,18 @@
      * @throws NullPointerException if the specified collection is either
      *            {@code null} or contains a {@code null} element
      */
-    private static void collectionNullClean(Collection<?> coll) {
-        boolean hasNullElements = false;
+    private static <E> LinkedList<E> collectionNullClean(
+            Collection<? extends E> coll) {
 
         Objects.requireNonNull(coll,
                 ResourcesMgr.getString("invalid.null.input.s."));
 
-        try {
-            hasNullElements = coll.contains(null);
-        } catch (NullPointerException npe) {
-            // A null-hostile collection may choose to throw
-            // NullPointerException if contains(null) is called on it
-            // rather than returning false.
-            // If this happens we know the collection is null-clean.
-            hasNullElements = false;
-        } finally {
-            if (hasNullElements) {
-                throw new NullPointerException
-                    (ResourcesMgr.getString("invalid.null.input.s."));
-            }
+        LinkedList<E> output = new LinkedList<>();
+        for (E e : coll) {
+            output.add(Objects.requireNonNull(e,
+                    ResourcesMgr.getString("invalid.null.input.s.")));
         }
+        return output;
     }
 
     /**
@@ -1066,10 +1061,10 @@
             this.elements = new LinkedList<E>();
         }
 
-        SecureSet(Subject subject, int which, Set<? extends E> set) {
+        SecureSet(Subject subject, int which, LinkedList<E> list) {
             this.subject = subject;
             this.which = which;
-            this.elements = new LinkedList<E>(set);
+            this.elements = list;
         }
 
         public int size() {
@@ -1242,7 +1237,7 @@
         public boolean addAll(Collection<? extends E> c) {
             boolean result = false;
 
-            collectionNullClean(c);
+            c = collectionNullClean(c);
 
             for (E item : c) {
                 result |= this.add(item);
@@ -1252,7 +1247,7 @@
         }
 
         public boolean removeAll(Collection<?> c) {
-            collectionNullClean(c);
+            c = collectionNullClean(c);
 
             boolean modified = false;
             final Iterator<E> e = iterator();
@@ -1282,7 +1277,7 @@
         }
 
         public boolean containsAll(Collection<?> c) {
-            collectionNullClean(c);
+            c = collectionNullClean(c);
 
             for (Object item : c) {
                 if (this.contains(item) == false) {
@@ -1294,7 +1289,7 @@
         }
 
         public boolean retainAll(Collection<?> c) {
-            collectionNullClean(c);
+            c = collectionNullClean(c);
 
             boolean modified = false;
             final Iterator<E> e = iterator();
@@ -1314,8 +1309,8 @@
                 if (c.contains(next) == false) {
                     e.remove();
                     modified = true;
-                    }
                 }
+            }
 
             return modified;
         }
@@ -1443,13 +1438,7 @@
 
             LinkedList<E> tmp = (LinkedList<E>) fields.get("elements", null);
 
-            Subject.collectionNullClean(tmp);
-
-            if (tmp.getClass() != LinkedList.class) {
-                elements = new LinkedList<E>(tmp);
-            } else {
-                elements = tmp;
-            }
+            elements = Subject.collectionNullClean(tmp);
         }
 
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/javax/security/auth/Subject/UnreliableContains.java	Thu Jul 09 09:22:01 2020 +0800
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8243592
+ * @summary Subject$SecureSet::addAll should not call contains(null)
+ */
+
+import javax.security.auth.Subject;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Objects;
+
+public class UnreliableContains {
+
+    public static void main(String[] args) {
+        MySet<Principal> set = new MySet<>();
+        set.add(null);
+        Subject s = null;
+        try {
+            s = new Subject(false, set, Collections.emptySet(),
+                    Collections.emptySet());
+        } catch (NullPointerException e) {
+            // The correct exit
+            return;
+        }
+        // Suppose NPE was not caught. At least null was not added?
+        for (Principal p : s.getPrincipals()) {
+            Objects.requireNonNull(p);
+        }
+        // Still must fail. We don't want this Subject created
+        throw new RuntimeException("Fail");
+    }
+
+    // This is a Map that implements contains(null) differently
+    static class MySet<E> extends HashSet<E> {
+        @Override
+        public boolean contains(Object o) {
+            if (o == null) {
+                return false;
+            } else {
+                return super.contains(o);
+            }
+        }
+    }
+}