Skip to content

Commit 4e60c2d

Browse files
committedFeb 19, 2025
8349699: XSL transform fails with certain UTF-8 characters on 1024 byte boundaries
Reviewed-by: lancea, naoto
1 parent 7734f8e commit 4e60c2d

File tree

2 files changed

+125
-6
lines changed
  • src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer
  • test/jaxp/javax/xml/jaxp/unittest/transform

2 files changed

+125
-6
lines changed
 

‎src/java.xml/share/classes/com/sun/org/apache/xml/internal/serializer/ToStream.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved.
33
*/
44
/*
55
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -55,7 +55,7 @@
5555
* serializers (xml, html, text ...) that write output to a stream.
5656
*
5757
* @xsl.usage internal
58-
* @LastModified: Mar 2022
58+
* @LastModified: Feb 2025
5959
*/
6060
abstract public class ToStream extends SerializerBase {
6161

@@ -955,7 +955,7 @@ protected int writeUTF16Surrogate(char c, char ch[], int i, int end)
955955
throws IOException, SAXException
956956
{
957957
int status = -1;
958-
if (i + 1 >= end)
958+
if (i + 1 >= end && m_highSurrogate == 0)
959959
{
960960
m_highSurrogate = c;
961961
return status;

‎test/jaxp/javax/xml/jaxp/unittest/transform/JDK8207760.java

+122-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -41,10 +41,10 @@
4141

4242
/*
4343
* @test
44+
* @bug 8207760 8349699
45+
* @summary Verifies that a surrogate pair at the edge of a buffer is properly handled
4446
* @library /javax/xml/jaxp/libs /javax/xml/jaxp/unittest
4547
* @run testng/othervm transform.JDK8207760
46-
* @summary Verifies that a surrogate pair at the edge of a buffer is properly handled
47-
* @bug 8207760
4848
*/
4949
public class JDK8207760 {
5050
final String xsl8207760 =
@@ -93,6 +93,14 @@ public class JDK8207760 {
9393
"\n" +
9494
"</xsl:stylesheet>";
9595

96+
final String xsl8349699 = """
97+
<?xml version="1.0" encoding="UTF-8"?>
98+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
99+
<xsl:output encoding="UTF-8" method="text" />
100+
<xsl:template match="/"><xsl:apply-templates select="node()" /></xsl:template>
101+
</xsl:stylesheet>
102+
""";
103+
96104
@DataProvider(name = "xsls")
97105
public Object[][] getDataBug8207760_cdata() {
98106
return new Object[][]{
@@ -101,6 +109,117 @@ public Object[][] getDataBug8207760_cdata() {
101109
};
102110
}
103111

112+
/*
113+
* Data for verifying the patch for JDK8349699
114+
* @see testBug8349699
115+
*/
116+
@DataProvider(name = "surrogatePair")
117+
public Object[][] getDataFor8349699() {
118+
return new Object[][]{
119+
// a surrogate pair in an XML element placed anywhere in a string
120+
{getXML(1024, 1024, "<b>\uD835\uDF00</b>"), getString(1024, 1024, "\uD835\uDF00")},
121+
{getXML(1023, 1023, "<b>\uD835\uDF00</b>"), getString(1023, 1023, "\uD835\uDF00")},
122+
{getXML(1023,0, "<b>\uD835\uDF00</b>"), getString(1023,0, "\uD835\uDF00")},
123+
{getXML(1023,120, "<b>\uD835\uDF00</b>"), getString(1023,120, "\uD835\uDF00")},
124+
// this is the original test as demonstrated in the bug report
125+
{getXML(1017,1017, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uD835\uDF00</b>\uD835\uDF00"),
126+
getString(1017,1017, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uDF00\uD835\uDF00")},
127+
{getXML(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uD835\uDF00</b>\uD835\uDF00"),
128+
getString(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uDF00\uD835\uDF00")},
129+
{getXML(1017,120, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uD835\uDF00</b>\uD835\uDF00"),
130+
getString(1017,120, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uDF00\uD835\uDF00")},
131+
};
132+
}
133+
134+
/*
135+
* Data for verifying the patch for JDK8349699
136+
* @see testBug8349699N
137+
*/
138+
@DataProvider(name = "invalidSurrogatePair")
139+
public Object[][] getDataFor8349699N() {
140+
return new Object[][]{
141+
// invalid pair: high/high
142+
{getXML(1024, 1024, "<b>\uD835\uD835</b>")},
143+
{getXML(1023, 1023, "<b>\uD835\uD835</b>")},
144+
{getXML(1023,0, "<b>\uD835\uD835</b>")},
145+
{getXML(1023,120, "<b>\uD835\uD835</b>")},
146+
// invalid pair: low/low
147+
{getXML(1024, 1024, "<b>\uDF00\uDF00</b>")},
148+
{getXML(1023, 1023, "<b>\uDF00\uDF00</b>")},
149+
{getXML(1023,0, "<b>\uDF00\uDF00</b>")},
150+
{getXML(1023,120, "<b>\uDF00\uDF00</b>")},
151+
// invalid pair in the original test string
152+
{getXML(1017,1017, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uD835\uD835</b>\uD835\uDF00")},
153+
{getXML(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uD835\uD835</b>\uD835\uDF00")},
154+
{getXML(1017,120, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uD835\uD835</b>\uD835\uDF00")},
155+
{getXML(1017,1017, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uDF00\uDF00</b>\uD835\uDF00")},
156+
{getXML(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uDF00\uDF00</b>\uD835\uDF00")},
157+
{getXML(1017,120, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uDF00\uDF00</b>\uD835\uDF00")},
158+
};
159+
}
160+
161+
/*
162+
* @bug 8349699
163+
* Verifies that a surrogate pair at the edge of a buffer is properly handled
164+
* when serializing into a Character section.
165+
*/
166+
@Test(dataProvider = "surrogatePair")
167+
public final void testBug8349699(String xml, String expected) throws Exception {
168+
Transformer t = createTransformerFromInputstream(
169+
new ByteArrayInputStream(xsl8349699.getBytes(StandardCharsets.UTF_8)));
170+
StringWriter sw = new StringWriter();
171+
t.transform(new StreamSource(new StringReader(xml)), new StreamResult(sw));
172+
Assert.assertEquals(sw.toString(), expected);
173+
}
174+
175+
/*
176+
* @bug 8349699
177+
* Verifies that invalid surrogate pairs are caught.
178+
*/
179+
@Test(dataProvider = "invalidSurrogatePair")
180+
public final void testBug8349699N(String xml) throws Exception {
181+
Assert.assertThrows(TransformerException.class, () -> {
182+
Transformer t = createTransformerFromInputstream(
183+
new ByteArrayInputStream(xsl8349699.getBytes(StandardCharsets.UTF_8)));
184+
StringWriter sw = new StringWriter();
185+
t.transform(new StreamSource(new StringReader(xml)), new StreamResult(sw));
186+
});
187+
}
188+
189+
/**
190+
* Returns an XML with the input string inserted in a text of length 'len' at
191+
* the position 'pos'.
192+
* @param len the length of the text to be placed in the XML
193+
* @param pos the position at which the input string will be inserted into the text
194+
* @param input the input string
195+
* @return an XML
196+
*/
197+
private String getXML(int len, int pos, String input) {
198+
StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" ?><a>");
199+
sb.append(getString(len, pos, input));
200+
sb.append("</a>");
201+
return sb.toString();
202+
}
203+
204+
/**
205+
* Returns a text string with the input string inserted at the specified position.
206+
* @param len the length of the text to be returned
207+
* @param pos the position at which the input string will be inserted into the text
208+
* @param input the input string
209+
* @return a text string
210+
*/
211+
private String getString(int len, int pos, String input) {
212+
StringBuilder sb = new StringBuilder();
213+
if (pos == 0) {
214+
sb.append(input).append("x".repeat(len));
215+
} else if (pos == len) {
216+
sb.append("x".repeat(len)).append(input);
217+
} else {
218+
sb.append("x".repeat(pos)).append(input).append("x".repeat(len - pos));
219+
}
220+
return sb.toString();
221+
}
222+
104223
/*
105224
* @bug 8207760
106225
* Verifies that a surrogate pair at the edge of a buffer is properly handled

0 commit comments

Comments
 (0)
Please sign in to comment.