Saturday, February 21, 2015

Ticking bomb in JDK 1.7.0_45 and blessing of soak tests

    Hello my friends! This is the first post in 2015 so it can be a little bit explosive. Actually, I have something that may be interesting for you if you like sleeping well. It sounds a little bit funny, doesn't it?
The application which I am going to talk about deals with XML. The request comes in, then it is processed and the result if forwarded to some endpoint. The overall condition of the application is really good and the number of defects found during regular testing is kept on a low level. Despite that fact we decided to perform some soak tests. As a result the application should process thousands of messages successfully. Everything went well for some time. However, after some time we observed a massive number of exceptions in logs. Their stack traces were like that one:
Exception in thread "processing-thread" javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,1]
Message: JAXP00010001: The parser has encountered more than "64000" entity 
expansions in this document; this is the limit imposed by the JDK.
at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.setInputSource(XMLStreamReaderImpl.java:219)
at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.(XMLStreamReaderImpl.java:189)
at com.sun.xml.internal.stream.XMLInputFactoryImpl.getXMLStreamReaderImpl(XMLInputFactoryImpl.java:277)
at com.sun.xml.internal.stream.XMLInputFactoryImpl.createXMLStreamReader(XMLInputFactoryImpl.java:129)
at com.sun.xml.internal.stream.XMLInputFactoryImpl.createXMLEventReader(XMLInputFactoryImpl.java:78)
It was very surprising. Some googling revealed the truth. It turned out to be a know bug in JDK 1.7.0_45. After calling createXMLEventReader method of XMLInputFactory 64000 times the application became useless and only restart could help. It seems that DOS attack protection implementation went bad and the counter used for that protection was global rather than local. Anyhow, the fix was really easy:
System.setProperty("jdk.xml.entityExpansionLimit", "0");
On the other hand we lost this DOS attack "pseudo protection". That bug was fixed in the newer versions of JDK. If you want to check if your JDK version is vulnerable you can execute the code:
import java.io.ByteArrayInputStream; 
import javax.xml.stream.XMLInputFactory;
 
public class Main {
    public static void main(String... args) throws Exception {
        final String xml = "<?xml version=\"1.0\"?><whatever/>";
        final XMLInputFactory factory = XMLInputFactory.newInstance();
        for (int i = 0; i < 64001; i++) {
            ByteArrayInputStream stream = new ByteArrayInputStream(xml.getBytes());
            factory.createXMLEventReader(stream);
        }
    }
} 
If you see that nasty exception and your application (or one of the libraries) calls the fragile method you are not safe. Can you imagine the consequences of that bug?

No comments :

Post a Comment