JDK-8212178 : Soft reference reclamation race in com.sun.xml.internal.stream.util.ThreadLocalBufferAllocator
  • Type: Bug
  • Component: xml
  • Sub-Component: javax.xml.stream
  • Affected Version: 8,11,12
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2018-10-15
  • Updated: 2019-09-04
  • Resolved: 2018-10-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 12 Other
11.0.2Fixed 12 b16Fixed openjdk8u212Fixed
Description
In ThreadLocalBufferAllocator.getBufferAllocator() there is no guarantee that the SoftReference doesn't get cleared between its construction and its later use of bAllocatorRef.get(). Therefore the method can return null. Calling code (e.g. UTF8Reader.<init>() ) does expects to get non-null though and can crash with NPE.

   public static BufferAllocator getBufferAllocator() {
        SoftReference<BufferAllocator> bAllocatorRef = tlba.get();
        if (bAllocatorRef == null || bAllocatorRef.get() == null) {
            bAllocatorRef = new SoftReference<>(new BufferAllocator());
            tlba.set(bAllocatorRef);
        }
 
        return bAllocatorRef.get(); // <--- returns null, because soft-ref was already cleared
   }

This semi-reliably crashes SPECjvm2008 XML tests with Shenandoah and aggressive mode that does back-to-back cycles.

Example fix that keeps return value reachable:

   public static BufferAllocator getBufferAllocator() {
        BufferAllocator ba = null;

        SoftReference<BufferAllocator> bAllocatorRef = tlba.get();
        if (bAllocatorRef != null) {
            ba = bAllocatorRef.get();
        }

        if (ba == null) {
            ba = new BufferAllocator();
            bAllocatorRef = new SoftReference<>(ba);
            tlba.set(bAllocatorRef);
        }

        return ba;
   }
Comments
8u RFR: https://mail.openjdk.java.net/pipermail/jdk8u-dev/2019-February/008518.html
06-02-2019

Please add a suitable noreg label.
18-10-2018

Fix Request This fix makes XML applications more resilient under frequent GCs. While caught with Shenandoah, it can manifest with any other GC that does reference processing. Having this simple fix in 11u seems profitable. The patch applies cleanly.
17-10-2018

Better fix: http://cr.openjdk.java.net/~shade/8212178/webrev.01/
15-10-2018

RFR: http://mail.openjdk.java.net/pipermail/core-libs-dev/2018-October/056063.html
15-10-2018