diff --git a/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToStream.java b/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToStream.java index fc01f006e97..7bff17a455f 100644 --- a/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToStream.java +++ b/src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved. */ /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -55,7 +55,7 @@ import org.xml.sax.SAXException; * serializers (xml, html, text ...) that write output to a stream. * * @xsl.usage internal - * @LastModified: Mar 2022 + * @LastModified: Feb 2025 */ abstract public class ToStream extends SerializerBase { @@ -955,7 +955,7 @@ abstract public class ToStream extends SerializerBase { throws IOException, SAXException { int status = -1; - if (i + 1 >= end) + if (i + 1 >= end && m_highSurrogate == 0) { m_highSurrogate = c; return status; diff --git a/test/jaxp/javax/xml/jaxp/unittest/transform/JDK8207760.java b/test/jaxp/javax/xml/jaxp/unittest/transform/JDK8207760.java index 00c1b8a8e25..6055fa878bc 100644 --- a/test/jaxp/javax/xml/jaxp/unittest/transform/JDK8207760.java +++ b/test/jaxp/javax/xml/jaxp/unittest/transform/JDK8207760.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, 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 @@ -41,10 +41,10 @@ import org.testng.annotations.DataProvider; /* * @test + * @bug 8207760 8349699 + * @summary Verifies that a surrogate pair at the edge of a buffer is properly handled * @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest * @run testng/othervm transform.JDK8207760 - * @summary Verifies that a surrogate pair at the edge of a buffer is properly handled - * @bug 8207760 */ public class JDK8207760 { final String xsl8207760 = @@ -93,6 +93,14 @@ public class JDK8207760 { "\n" + ""; + final String xsl8349699 = """ + + + + + + """; + @DataProvider(name = "xsls") public Object[][] getDataBug8207760_cdata() { return new Object[][]{ @@ -101,6 +109,117 @@ public class JDK8207760 { }; } + /* + * Data for verifying the patch for JDK8349699 + * @see testBug8349699 + */ + @DataProvider(name = "surrogatePair") + public Object[][] getDataFor8349699() { + return new Object[][]{ + // a surrogate pair in an XML element placed anywhere in a string + {getXML(1024, 1024, "\uD835\uDF00"), getString(1024, 1024, "\uD835\uDF00")}, + {getXML(1023, 1023, "\uD835\uDF00"), getString(1023, 1023, "\uD835\uDF00")}, + {getXML(1023,0, "\uD835\uDF00"), getString(1023,0, "\uD835\uDF00")}, + {getXML(1023,120, "\uD835\uDF00"), getString(1023,120, "\uD835\uDF00")}, + // this is the original test as demonstrated in the bug report + {getXML(1017,1017, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uDF00\uD835\uDF00"), + getString(1017,1017, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uDF00\uD835\uDF00")}, + {getXML(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uDF00\uD835\uDF00"), + getString(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uDF00\uD835\uDF00")}, + {getXML(1017,120, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uDF00\uD835\uDF00"), + getString(1017,120, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uDF00\uD835\uDF00")}, + }; + } + + /* + * Data for verifying the patch for JDK8349699 + * @see testBug8349699N + */ + @DataProvider(name = "invalidSurrogatePair") + public Object[][] getDataFor8349699N() { + return new Object[][]{ + // invalid pair: high/high + {getXML(1024, 1024, "\uD835\uD835")}, + {getXML(1023, 1023, "\uD835\uD835")}, + {getXML(1023,0, "\uD835\uD835")}, + {getXML(1023,120, "\uD835\uD835")}, + // invalid pair: low/low + {getXML(1024, 1024, "\uDF00\uDF00")}, + {getXML(1023, 1023, "\uDF00\uDF00")}, + {getXML(1023,0, "\uDF00\uDF00")}, + {getXML(1023,120, "\uDF00\uDF00")}, + // invalid pair in the original test string + {getXML(1017,1017, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uD835\uD835\uDF00")}, + {getXML(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uD835\uD835\uDF00")}, + {getXML(1017,120, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uD835\uD835\uDF00")}, + {getXML(1017,1017, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uDF00\uDF00\uD835\uDF00")}, + {getXML(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uDF00\uDF00\uD835\uDF00")}, + {getXML(1017,120, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uDF00\uDF00\uD835\uDF00")}, + }; + } + + /* + * @bug 8349699 + * Verifies that a surrogate pair at the edge of a buffer is properly handled + * when serializing into a Character section. + */ + @Test(dataProvider = "surrogatePair") + public final void testBug8349699(String xml, String expected) throws Exception { + Transformer t = createTransformerFromInputstream( + new ByteArrayInputStream(xsl8349699.getBytes(StandardCharsets.UTF_8))); + StringWriter sw = new StringWriter(); + t.transform(new StreamSource(new StringReader(xml)), new StreamResult(sw)); + Assert.assertEquals(sw.toString(), expected); + } + + /* + * @bug 8349699 + * Verifies that invalid surrogate pairs are caught. + */ + @Test(dataProvider = "invalidSurrogatePair") + public final void testBug8349699N(String xml) throws Exception { + Assert.assertThrows(TransformerException.class, () -> { + Transformer t = createTransformerFromInputstream( + new ByteArrayInputStream(xsl8349699.getBytes(StandardCharsets.UTF_8))); + StringWriter sw = new StringWriter(); + t.transform(new StreamSource(new StringReader(xml)), new StreamResult(sw)); + }); + } + + /** + * Returns an XML with the input string inserted in a text of length 'len' at + * the position 'pos'. + * @param len the length of the text to be placed in the XML + * @param pos the position at which the input string will be inserted into the text + * @param input the input string + * @return an XML + */ + private String getXML(int len, int pos, String input) { + StringBuilder sb = new StringBuilder(""); + sb.append(getString(len, pos, input)); + sb.append(""); + return sb.toString(); + } + + /** + * Returns a text string with the input string inserted at the specified position. + * @param len the length of the text to be returned + * @param pos the position at which the input string will be inserted into the text + * @param input the input string + * @return a text string + */ + private String getString(int len, int pos, String input) { + StringBuilder sb = new StringBuilder(); + if (pos == 0) { + sb.append(input).append("x".repeat(len)); + } else if (pos == len) { + sb.append("x".repeat(len)).append(input); + } else { + sb.append("x".repeat(pos)).append(input).append("x".repeat(len - pos)); + } + return sb.toString(); + } + /* * @bug 8207760 * Verifies that a surrogate pair at the edge of a buffer is properly handled