Fix race condition in java.security.Provider

The supportedFormats and supportedClasses arrays are supposed to
be set only once inside the hasKeyAttributes() function. This is
guarded by the volatile flag hasKeyAttributes, however there was
a race condition when multiple threads check the flag
concurrently and then reach the synchronized block. In that case
the attributes might get set multiple times, concurrent with
reads.

This change fixes this by checking again the guard flag once
inside the synchronized block.

Bug: 276503829
Test: atest CtsLibcoreOjTestCases
Change-Id: Ic544033c1e49e823564c8a05ab3df50286e4c2e0
diff --git a/ojluni/src/main/java/java/security/Provider.java b/ojluni/src/main/java/java/security/Provider.java
index 6deb954..8239cb7 100644
--- a/ojluni/src/main/java/java/security/Provider.java
+++ b/ojluni/src/main/java/java/security/Provider.java
@@ -1793,28 +1793,35 @@
             Boolean b = hasKeyAttributes;
             if (b == null) {
                 synchronized (this) {
-                    String s;
-                    s = getAttribute("SupportedKeyFormats");
-                    if (s != null) {
-                        supportedFormats = s.split("\\|");
-                    }
-                    s = getAttribute("SupportedKeyClasses");
-                    if (s != null) {
-                        String[] classNames = s.split("\\|");
-                        List<Class<?>> classList =
-                            new ArrayList<>(classNames.length);
-                        for (String className : classNames) {
-                            Class<?> clazz = getKeyClass(className);
-                            if (clazz != null) {
-                                classList.add(clazz);
-                            }
+                    // BEGIN Android-changed: Double-check idiom for concurrent access b/276503829.
+                    // This implementation is brought in line with the upstream commit
+                    // 0610992a8fd46aac2df5e535ad3924a1dca248c4.
+                    b = hasKeyAttributes;
+                    if (b == null) {
+                        String s;
+                        s = getAttribute("SupportedKeyFormats");
+                        if (s != null) {
+                            supportedFormats = s.split("\\|");
                         }
-                        supportedClasses = classList.toArray(CLASS0);
+                        s = getAttribute("SupportedKeyClasses");
+                        if (s != null) {
+                            String[] classNames = s.split("\\|");
+                            List<Class<?>> classList =
+                                new ArrayList<>(classNames.length);
+                            for (String className : classNames) {
+                                Class<?> clazz = getKeyClass(className);
+                                if (clazz != null) {
+                                    classList.add(clazz);
+                                }
+                            }
+                            supportedClasses = classList.toArray(CLASS0);
+                        }
+                        boolean bool = (supportedFormats != null)
+                            || (supportedClasses != null);
+                        b = Boolean.valueOf(bool);
+                        hasKeyAttributes = b;
                     }
-                    boolean bool = (supportedFormats != null)
-                        || (supportedClasses != null);
-                    b = Boolean.valueOf(bool);
-                    hasKeyAttributes = b;
+                    // END Android-changed: Double-check idiom for concurrent access b/276503829.
                 }
             }
             return b.booleanValue();