8349699: XSL transform fails with certain UTF-8 characters on 1024 byte boundaries

Reviewed-by: lancea, naoto
This commit is contained in:
Joe Wang 2025-02-19 19:36:30 +00:00
parent 7734f8ed13
commit 4e60c2d937
2 changed files with 125 additions and 6 deletions

View File

@ -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;

View File

@ -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" +
"</xsl:stylesheet>";
final String xsl8349699 = """
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" method="text" />
<xsl:template match="/"><xsl:apply-templates select="node()" /></xsl:template>
</xsl:stylesheet>
""";
@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, "<b>\uD835\uDF00</b>"), getString(1024, 1024, "\uD835\uDF00")},
{getXML(1023, 1023, "<b>\uD835\uDF00</b>"), getString(1023, 1023, "\uD835\uDF00")},
{getXML(1023,0, "<b>\uD835\uDF00</b>"), getString(1023,0, "\uD835\uDF00")},
{getXML(1023,120, "<b>\uD835\uDF00</b>"), 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<b>\uD835\uDF00</b>\uD835\uDF00"),
getString(1017,1017, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uDF00\uD835\uDF00")},
{getXML(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uD835\uDF00</b>\uD835\uDF00"),
getString(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00\uD835\uDF00\uD835\uDF00")},
{getXML(1017,120, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uD835\uDF00</b>\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, "<b>\uD835\uD835</b>")},
{getXML(1023, 1023, "<b>\uD835\uD835</b>")},
{getXML(1023,0, "<b>\uD835\uD835</b>")},
{getXML(1023,120, "<b>\uD835\uD835</b>")},
// invalid pair: low/low
{getXML(1024, 1024, "<b>\uDF00\uDF00</b>")},
{getXML(1023, 1023, "<b>\uDF00\uDF00</b>")},
{getXML(1023,0, "<b>\uDF00\uDF00</b>")},
{getXML(1023,120, "<b>\uDF00\uDF00</b>")},
// invalid pair in the original test string
{getXML(1017,1017, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uD835\uD835</b>\uD835\uDF00")},
{getXML(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uD835\uD835</b>\uD835\uDF00")},
{getXML(1017,120, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uD835\uD835</b>\uD835\uDF00")},
{getXML(1017,1017, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uDF00\uDF00</b>\uD835\uDF00")},
{getXML(1017,0, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uDF00\uDF00</b>\uD835\uDF00")},
{getXML(1017,120, "\uD835\uDF03\uD835\uDF00\uD835\uDF00<b>\uDF00\uDF00</b>\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("<?xml version=\"1.0\" ?><a>");
sb.append(getString(len, pos, input));
sb.append("</a>");
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