commits@javamail.java.net

[javamail~mercurial:804] CollectorFormatter descending order data race - Bug 7083

From: <shannon_at_java.net>
Date: Wed, 2 Dec 2015 23:50:20 +0000

Project: javamail
Repository: mercurial
Revision: 804
Author: shannon
Date: 2015-12-02 21:30:36 UTC
Link:

Log Message:
------------
skip terminating ')' in string list - bug 7090
CollectorFormatter descending order data race - Bug 7083


Revisions:
----------
803
804


Modified Paths:
---------------
doc/release/CHANGES.txt
mail/src/main/java/com/sun/mail/iap/Response.java
mail/src/test/java/com/sun/mail/iap/ResponseTest.java
mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java
mail/src/test/java/com/sun/mail/util/logging/CollectorFormatterTest.java


Diffs:
------
diff -r 462b6507d18a -r 1e8457095a27 doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Fri Nov 20 15:58:27 2015 -0800
+++ b/doc/release/CHANGES.txt Wed Dec 02 13:02:12 2015 -0800
@@ -42,6 +42,8 @@
 K 7052 Empty Gmail X-GM-LABELS list is misparsed
 K 7075 IMAPMessage.getReceivedDate should check if receivedDate is present
         before loading envelope
+K 7090 off-by-1 error in Response.readStringList causes early termination of
+ parsing FETCH response
 
 
                   CHANGES IN THE 1.5.4 RELEASE

diff -r 462b6507d18a -r 1e8457095a27 mail/src/main/java/com/sun/mail/iap/Response.java
--- a/mail/src/main/java/com/sun/mail/iap/Response.java Fri Nov 20 15:58:27 2015 -0800
+++ b/mail/src/main/java/com/sun/mail/iap/Response.java Wed Dec 02 13:02:12 2015 -0800
@@ -292,8 +292,9 @@
         if (peekByte() != ')') {
             do {
                 result.add(atom ? readAtomString() : readString());
- } while (buffer[index++] != ')');
- }
+ } while (index < size && buffer[index++] != ')');
+ } else
+ index++; // skip ')'
 
         return result.toArray(new String[result.size()]);
     }

diff -r 462b6507d18a -r 1e8457095a27 mail/src/test/java/com/sun/mail/iap/ResponseTest.java
--- a/mail/src/test/java/com/sun/mail/iap/ResponseTest.java Fri Nov 20 15:58:27 2015 -0800
+++ b/mail/src/test/java/com/sun/mail/iap/ResponseTest.java Wed Dec 02 13:02:12 2015 -0800
@@ -109,6 +109,17 @@
     }
 
     /**
+ * Test astring lists with more data following.
+ */
+ @Test
+ public void testAStringListMore() throws Exception {
+ Response r = new Response("* " + "(A B \"C\") atom");
+ assertArrayEquals(new String[] { "A", "B", "C" },
+ r.readAtomStringList());
+ assertEquals("atom", r.readAtomString());
+ }
+
+ /**
      * Test empty astring lists.
      */
     @Test
@@ -116,4 +127,14 @@
         Response r = new Response("* " + "()");
         assertArrayEquals(new String[0], r.readAtomStringList());
     }
+
+ /**
+ * Test empty astring lists with more data following.
+ */
+ @Test
+ public void testAStringListEmptyMore() throws Exception {
+ Response r = new Response("* " + "() atom");
+ assertArrayEquals(new String[0], r.readAtomStringList());
+ assertEquals("atom", r.readAtomString());
+ }
 }


diff -r 1e8457095a27 -r 122c7b3a6fdb doc/release/CHANGES.txt
--- a/doc/release/CHANGES.txt Wed Dec 02 13:02:12 2015 -0800
+++ b/doc/release/CHANGES.txt Wed Dec 02 13:30:36 2015 -0800
@@ -42,6 +42,7 @@
 K 7052 Empty Gmail X-GM-LABELS list is misparsed
 K 7075 IMAPMessage.getReceivedDate should check if receivedDate is present
         before loading envelope
+K 7083 CollectorFormatter descending order data race
 K 7090 off-by-1 error in Response.readStringList causes early termination of
         parsing FETCH response
 

diff -r 1e8457095a27 -r 122c7b3a6fdb mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java
--- a/mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java Wed Dec 02 13:02:12 2015 -0800
+++ b/mail/src/main/java/com/sun/mail/util/logging/CollectorFormatter.java Wed Dec 02 13:30:36 2015 -0800
@@ -209,8 +209,7 @@
                 update.getSourceMethodName(); //Infer caller, null check.
                 accepted = acceptAndUpdate(peek, update);
             } else {
- accepted = true;
- accept(record);
+ accepted = accept(peek, record);
             }
         } while (!accepted);
         return "";
@@ -368,21 +367,36 @@
     }
 
     /**
- * Updates the summary statistics but does not store the given LogRecord.
+ * Updates the summary statistics only if the expected record matches the
+ * last record. The update record is not stored.
      *
- * @param record the LogRecord used to collect statistics.
+ * @param e the LogRecord that is expected.
+ * @param u the LogRecord used to collect statistics.
+ * @return true if the last record was the expected record.
+ * @throws NullPointerException if the update record is null.
      */
- private synchronized void accept(final LogRecord record) {
- final long millis = record.getMillis();
- if (++count != 1L) {
- minMillis = Math.min(minMillis, millis);
- } else { //Show single records as instant and not a time period.
- minMillis = millis;
- }
- maxMillis = Math.max(maxMillis, millis);
+ private synchronized boolean accept(final LogRecord e, final LogRecord u) {
+ /**
+ * LogRecord methods must be called before the check of the last stored
+ * record to guard against subclasses of LogRecord that might attempt to
+ * reset the state by triggering a call to getTail.
+ */
+ final long millis = u.getMillis(); //Null check.
+ final Throwable ex = u.getThrown();
+ if (last == e) { //Only if the exact same reference.
+ if (++count != 1L) {
+ minMillis = Math.min(minMillis, millis);
+ } else { //Show single records as instant and not a time period.
+ minMillis = millis;
+ }
+ maxMillis = Math.max(maxMillis, millis);
 
- if (record.getThrown() != null) {
- ++thrown;
+ if (ex != null) {
+ ++thrown;
+ }
+ return true;
+ } else {
+ return false;
         }
     }
 
@@ -501,10 +515,10 @@
      * @param e the expected record.
      * @param u the update record.
      * @return true if the update was performed.
+ * @throws NullPointerException if the update record is null.
      */
     private synchronized boolean acceptAndUpdate(LogRecord e, LogRecord u) {
- if (e == this.last) {
- accept(u);
+ if (accept(e, u)) {
             this.last = u;
             return true;
         } else {

diff -r 1e8457095a27 -r 122c7b3a6fdb mail/src/test/java/com/sun/mail/util/logging/CollectorFormatterTest.java
--- a/mail/src/test/java/com/sun/mail/util/logging/CollectorFormatterTest.java Wed Dec 02 13:02:12 2015 -0800
+++ b/mail/src/test/java/com/sun/mail/util/logging/CollectorFormatterTest.java Wed Dec 02 13:30:36 2015 -0800
@@ -154,6 +154,93 @@
         f.format((LogRecord) null);
     }
 
+ private static final class TestFormatterAccept extends LogRecord {
+
+ private static final long serialVersionUID = 1L;
+
+ int inferred;
+ private transient Formatter f;
+
+ TestFormatterAccept(Level level, CollectorFormatter f) {
+ super(level, "");
+ this.f = f;
+ }
+
+ @Override
+ public Throwable getThrown() {
+ if (inferred == 0) {
+ assertEquals(Level.INFO.getLocalizedName().concat("11111"),
+ f.getTail((Handler) null));
+ }
+ return super.getThrown();
+ }
+
+ @Override
+ public String getSourceMethodName() {
+ ++inferred;
+ return super.getSourceMethodName();
+ }
+ }
+
+ @Test(timeout = 60000)
+ public void testFormatAccept() throws Exception {
+ final CollectorFormatter f = new CollectorFormatter(
+ "{1}{3}{5}{7}{8}{13}",
+ new CompactFormatter("%4$s"),
+ new SeverityComparator());
+ LogRecord first = new LogRecord(Level.INFO, "");
+ first.setThrown(new Throwable());
+ first.setMillis(1L);
+ f.format(first);
+
+ TestFormatterAccept r = new TestFormatterAccept(Level.FINE, f);
+ r.setMillis(2L);
+ r.setThrown(new Throwable());
+ f.format(r);
+ assertEquals(1, r.inferred);
+ assertEquals(Level.FINE.getLocalizedName().concat("11222"),
+ f.getTail((Handler) null));
+ }
+
+ private static final class TestFormatAcceptAndUpdate extends LogRecord {
+
+ private static final long serialVersionUID = 1L;
+ int inferred;
+ private transient Formatter f;
+
+ public TestFormatAcceptAndUpdate(Level level, CollectorFormatter f) {
+ super(level, "");
+ this.f = f;
+ }
+
+ @Override
+ public String getSourceMethodName() {
+ if (++inferred == 1) {
+ LogRecord r = new LogRecord(Level.INFO, "");
+ r.setMillis(1L);
+ f.format(r);
+ }
+ return super.getSourceMethodName();
+ }
+ }
+
+ @Test(timeout = 60000)
+ public void testFormatAcceptAndUpdate() throws Exception {
+ final CollectorFormatter f = new CollectorFormatter(
+ "{1}{3}{5}{7}{8}{13}",
+ new CompactFormatter("%4$s"),
+ new SeverityComparator());
+
+ TestFormatAcceptAndUpdate r
+ = new TestFormatAcceptAndUpdate(Level.SEVERE, f);
+ r.setMillis(2L);
+ r.setThrown(new Throwable());
+ f.format(r);
+ assertEquals(2, r.inferred);
+ assertEquals(Level.SEVERE.getLocalizedName().concat("21121"),
+ f.getTail((Handler) null));
+ }
+
     @Test
     public void testFormatFormat() {
         String msg = "message";
@@ -575,7 +662,6 @@
         String output = cf.getTail((Handler) null);
         assertNotNull(output);
 
-
         cf.format(min);
         for (int i = 0; i < 114; ++i) {
             LogRecord mid = new LogRecord(Level.SEVERE, "");
@@ -583,7 +669,6 @@
             cf.format(mid);
         }
 
-
         max.setMillis(min.getMillis() + 2591000000L);
         cf.format(max);
 
@@ -816,7 +901,6 @@
         assertEquals(75, n.longValue());
     }
 
-
     @Test
     public void testFormatInitTimeMillis() throws Exception {
         CollectorFormatter f = new CollectorFormatter("{10}", (Formatter) null,
@@ -832,7 +916,7 @@
     @Test
     public void testFormatInitTimeDateTime() throws Exception {
         CollectorFormatter f = new CollectorFormatter(
- "{10,date,"+DATE_TIME_FMT+"}",
+ "{10,date," + DATE_TIME_FMT + "}",
                 (Formatter) null,
                 (Comparator<LogRecord>) null);
 
@@ -860,7 +944,7 @@
     @Test
     public void testFormatCurrentTimeDateTime() throws Exception {
         CollectorFormatter f = new CollectorFormatter(
- "{11,date,"+DATE_TIME_FMT+"}",
+ "{11,date," + DATE_TIME_FMT + "}",
                 (Formatter) null,
                 (Comparator<LogRecord>) null);
 
@@ -964,6 +1048,7 @@
      * An example of a broken implementation of apply.
      */
     private static class ApplyReturnsNull extends CollectorFormatter {
+
         /**
          * The number of records.
          */