8014477: (str) Race condition in String.contentEquals when comparing with StringBuffer
authorplevart
Fri, 17 May 2013 14:41:39 +0200
changeset 17698 ab37c47ff886
parent 17697 7ba520729c40
child 17699 dfa52f56753b
8014477: (str) Race condition in String.contentEquals when comparing with StringBuffer Reviewed-by: alanb, mduigou, dholmes
jdk/src/share/classes/java/lang/String.java
jdk/test/java/lang/String/StringContentEqualsBug.java
--- a/jdk/src/share/classes/java/lang/String.java	Sat May 18 18:55:56 2013 -0700
+++ b/jdk/src/share/classes/java/lang/String.java	Fri May 17 14:41:39 2013 +0200
@@ -1010,13 +1010,14 @@
     private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
         char v1[] = value;
         char v2[] = sb.getValue();
-        int i = 0;
-        int n = value.length;
-        while (n-- != 0) {
+        int n = v1.length;
+        if (n != sb.length()) {
+            return false;
+        }
+        for (int i = 0; i < n; i++) {
             if (v1[i] != v2[i]) {
                 return false;
             }
-            i++;
         }
         return true;
     }
@@ -1038,8 +1039,6 @@
      * @since  1.5
      */
     public boolean contentEquals(CharSequence cs) {
-        if (value.length != cs.length())
-            return false;
         // Argument is a StringBuffer, StringBuilder
         if (cs instanceof AbstractStringBuilder) {
             if (cs instanceof StringBuffer) {
@@ -1055,12 +1054,14 @@
             return true;
         // Argument is a generic CharSequence
         char v1[] = value;
-        int i = 0;
-        int n = value.length;
-        while (n-- != 0) {
-            if (v1[i] != cs.charAt(i))
+        int n = v1.length;
+        if (n != cs.length()) {
+            return false;
+        }
+        for (int i = 0; i < n; i++) {
+            if (v1[i] != cs.charAt(i)) {
                 return false;
-            i++;
+            }
         }
         return true;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/String/StringContentEqualsBug.java	Fri May 17 14:41:39 2013 +0200
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2013, 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 8014477
+ * @summary test String.contentEquals(StringBuffer)
+ */
+public class StringContentEqualsBug {
+
+    static abstract class Task extends Thread {
+        volatile StringBuffer sb;
+        volatile Exception exception;
+
+        Task(StringBuffer sb) {
+            this.sb = sb;
+        }
+
+        @Override
+        public void run() {
+            try {
+                StringBuffer sb;
+                while ((sb = this.sb) != null) {
+                    doWith(sb);
+                }
+            }
+            catch (Exception e) {
+                exception = e;
+            }
+        }
+
+        protected abstract void doWith(StringBuffer sb);
+    }
+
+    static class Tester extends Task {
+        Tester(StringBuffer sb) {
+            super(sb);
+        }
+
+        @Override
+        protected void doWith(StringBuffer sb) {
+            "AA".contentEquals(sb);
+        }
+    }
+
+    static class Disturber extends Task {
+        Disturber(StringBuffer sb) {
+            super(sb);
+        }
+
+        @Override
+        protected void doWith(StringBuffer sb) {
+            sb.setLength(0);
+            sb.trimToSize();
+            sb.append("AA");
+        }
+    }
+
+
+    public static void main(String[] args) throws Exception {
+        StringBuffer sb = new StringBuffer();
+        Task[] tasks = new Task[3];
+        (tasks[0] = new Tester(sb)).start();
+        for (int i = 1; i < tasks.length; i++) {
+            (tasks[i] = new Disturber(sb)).start();
+        }
+
+        try
+        {
+            // wait at most 5 seconds for any of the threads to throw exception
+            for (int i = 0; i < 20; i++) {
+                for (Task task : tasks) {
+                    if (task.exception != null) {
+                        throw task.exception;
+                    }
+                }
+                Thread.sleep(250L);
+            }
+        }
+        finally {
+            for (Task task : tasks) {
+                task.sb = null;
+                task.join();
+            }
+        }
+    }
+}