1 /*
   2  * Copyright 2014, SAP AG. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug 8034087 8027359
  27  * @summary XML parser may overwrite element content if that content falls onto the border of an entity scanner buffer
  28  * @run main FragmentScannerBufferLimitTest
  29  */
  30 import java.io.*;
  31 
  32 import javax.xml.parsers.*;
  33 import javax.xml.transform.*;
  34 import org.w3c.dom.*;
  35 import org.xml.sax.*;
  36 
  37 /**
  38  * Test for overwriting of XML element content by the XML parser when reading over buffer
  39  * limits.
  40  *
  41  * We create a simple XML document of the form:
  42  *
  43  * <?xml version=\"1.1\"?>
  44  * <ROOT>
  45  *    <FILLER>ffffffff...fffff</FILLER>
  46  *    <TEST>content</TEST><TEST2>content2</TEST2>
  47  *    <FILLER>ffffffff...fffffffff</FILLER>
  48  * </ROOT>
  49  *
  50  * What's important here is, that the test content is at the border of an entity scanner
  51  * buffer (XMLEntityScanner uses 8192 byte buffers). That's why there are filler elements
  52  * of sufficient length that ensure there is a buffer break inside the test content
  53  * and there is enough to read to require another buffer read after the content has been
  54  * read.
  55  *
  56  * With the faulty implementation, the test content gets overwritten with characters
  57  * read from the next buffer, i.e. 'f's.
  58  *
  59  * @author steffen.schreiber@sap.com
  60  */
  61 public class FragmentScannerBufferLimitTest {
  62 
  63     static int errCount = 0;
  64 
  65     /**
  66      * Check the test content.
  67      */
  68     public static void main(String[] args) throws ParserConfigurationException,
  69             SAXException, IOException, TransformerConfigurationException,
  70             TransformerException, TransformerFactoryConfigurationError {
  71 
  72         String testString = "<TEST>content</TEST><TEST2>content2</TEST2>";
  73 
  74         for (int i = 0; i < testString.length(); i++) {
  75             test(createDocument(testString.toString(), i), ""+ i);
  76         }
  77 
  78         if (errCount == 0) {
  79             System.out.println("OK");
  80         }
  81         else {
  82             System.out.println("ERROR");
  83             throw new RuntimeException("Parsing error: element content has been overwritten");
  84         }
  85     }
  86 
  87     /**
  88      * Create the test XML document.
  89      * @param testString the test content string
  90      * @param bufferLimitPosition the position in the string where the buffer should break
  91      * @return the document
  92      */
  93     private static String createDocument(String testString, int bufferLimitPosition) throws UnsupportedEncodingException {
  94         StringBuilder result = new StringBuilder();
  95         result.append("<?xml version=\"1.1\"?>");
  96         result.append("<ROOT>");
  97 
  98         int fillerLength = 8192 - bufferLimitPosition;
  99         createFiller(result, fillerLength);
 100 
 101         result.append(testString);
 102 
 103         createFiller(result, 9000);
 104         result.append("</ROOT>");
 105         return result.toString();
 106     }
 107 
 108     /**
 109      * Create the filler element of the given length.
 110      * @param buffer the output buffer
 111      * @param length the required length of the element, including the element tags
 112      */
 113     private static void createFiller(StringBuilder buffer, int length) {
 114         buffer.append("<FILLER>");
 115         int fillLength = length - "<FILLER></FILLER>".length();
 116         for (int i=0; i<fillLength; i++) {
 117             buffer.append('f');
 118         }
 119         buffer.append("</FILLER>");
 120     }
 121 
 122 
 123     private static void test(String document, String testName) throws SAXException, IOException, ParserConfigurationException {
 124         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 125         DocumentBuilder builder = factory.newDocumentBuilder();
 126 
 127         Document doc = builder.parse(new ByteArrayInputStream(document.getBytes("UTF-8")));
 128 
 129         // check that there is the root node
 130         NodeList roots = doc.getElementsByTagName("ROOT");
 131         assert roots.getLength() == 1;
 132         Node root = roots.item(0);
 133 
 134         // check that root has children "FILLER" and "TEST"
 135         NodeList children = root.getChildNodes();
 136         assert children.getLength() == 4;
 137         assert children.item(0).getNodeName().equals("FILLER");
 138         assert children.item(1).getNodeName().equals("TEST");
 139         assert children.item(2).getNodeName().equals("TEST2");
 140         assert children.item(3).getNodeName().equals("FILLER");
 141 
 142         // check that the test node has content "content"
 143         checkContent(children.item(1).getTextContent(), "content", document);
 144         checkContent(children.item(2).getTextContent(), "content2", document);
 145     }
 146 
 147     private static void checkContent(String found, String expected, String document) {
 148         if (! (found.equals(expected))) {
 149             errCount++;
 150             int bufferStart = "<?xml version=\"1.1\"?><ROOT>".length() +1;
 151             int bufferStart2 = bufferStart + 8192;
 152             System.err.println("\nError:: expected \"" + expected
 153                     + "\", but found \"" + found + "\"!");
 154             System.err.println("Buffer was (probably): [ ... "
 155                     + document.substring(bufferStart2 - 20, bufferStart2) + "] ["
 156                     + document.substring(bufferStart2, bufferStart2 + 30) + " ... ]");
 157         }
 158     }
 159 }