Skip to content

Commit d7ce61f

Browse files
authored
Fix possible IndexOutOfBoundsException for issue #618 (#619)
1 parent f5cc10a commit d7ce61f

File tree

10 files changed

+268
-133
lines changed

10 files changed

+268
-133
lines changed

release-notes/CREDITS-2.x

+7-1
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,14 @@ Marco Belladelli (mbladel@github)
238238
not working as expected
239239
(2.15.0)
240240

241-
Motonori IWAMURO (vmi@github)
241+
Motonori IWAMURO (@vmi)
242242

243243
* Contributed fix for #616: Fix mismatch in `setNextIsUnwrapped(boolean)` in
244244
`XmlBeanSerializerBase#serializeFieldsFiltered()`
245245
(2.16.1)
246+
247+
Arthur Chan (@arthurscchan)
248+
249+
* Reported, contributed fix for #618: `ArrayIndexOutOfBoundsException` thrown for invalid
250+
ending XML string when using JDK default Stax XML parser
251+
(2.17.0)

release-notes/VERSION-2.x

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ Project: jackson-dataformat-xml
66

77
2.17.0 (not yet released)
88

9-
-
9+
#618: `ArrayIndexOutOfBoundsException` thrown for invalid ending XML string
10+
when using JDK default Stax XML parser
11+
(reported by Arthur C)
1012

1113
2.16.1 (not yet released)
1214

src/main/java/com/fasterxml/jackson/dataformat/xml/XmlFactory.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import com.fasterxml.jackson.core.format.MatchStrength;
1414
import com.fasterxml.jackson.core.io.IOContext;
1515
import com.fasterxml.jackson.core.util.VersionUtil;
16-
16+
import com.fasterxml.jackson.databind.util.ClassUtil;
1717
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
1818
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
1919
import com.fasterxml.jackson.dataformat.xml.util.StaxUtil;
@@ -661,7 +661,17 @@ protected FromXmlParser _createParser(byte[] data, int offset, int len, IOContex
661661
if (_xmlInputFactory instanceof XMLInputFactory2) {
662662
sr = _xmlInputFactory.createXMLStreamReader(new Stax2ByteArraySource(data, offset, len));
663663
} else {
664-
sr = _xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(data, offset, len));
664+
// 04-Dec-2023, tatu: As per [dataformat-xml#618], JDK's crappy in-built
665+
// Stax implementation barfs here. Hence:
666+
try {
667+
sr = _xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(data, offset, len));
668+
} catch (ArrayIndexOutOfBoundsException e) {
669+
throw new JsonParseException(null,
670+
"Internal processing error by `XMLInputFactory` of type "
671+
+ClassUtil.classNameOf(_xmlInputFactory)+" when trying to create a parser ("
672+
+"consider using Woodstox instead): "
673+
+e.getMessage());
674+
}
665675
}
666676
} catch (XMLStreamException e) {
667677
return StaxUtil.throwAsParseException(e, null);

src/main/java/com/fasterxml/jackson/dataformat/xml/deser/XmlTokenStream.java

+10-6
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
import javax.xml.XMLConstants;
66
import javax.xml.stream.*;
77

8-
import com.fasterxml.jackson.dataformat.xml.XmlNameProcessor;
98
import org.codehaus.stax2.XMLStreamLocation2;
109
import org.codehaus.stax2.XMLStreamReader2;
11-
import org.codehaus.stax2.ri.Stax2ReaderAdapter;
1210

1311
import com.fasterxml.jackson.core.JsonLocation;
1412
import com.fasterxml.jackson.core.io.ContentReference;
1513

14+
import com.fasterxml.jackson.dataformat.xml.XmlNameProcessor;
15+
import com.fasterxml.jackson.dataformat.xml.util.Stax2JacksonReaderAdapter;
16+
1617
/**
1718
* Simple helper class used on top of STAX {@link XMLStreamReader} to further
1819
* abstract out all irrelevant details, and to expose equivalent of flat token
@@ -168,7 +169,8 @@ public XmlTokenStream(XMLStreamReader xmlReader, ContentReference sourceRef,
168169
_sourceReference = sourceRef;
169170
_formatFeatures = formatFeatures;
170171
_cfgProcessXsiNil = FromXmlParser.Feature.PROCESS_XSI_NIL.enabledIn(_formatFeatures);
171-
_xmlReader = Stax2ReaderAdapter.wrapIfNecessary(xmlReader);
172+
// 04-Dec-2023, tatu: [dataformat-xml#618] Need further customized adapter:
173+
_xmlReader = Stax2JacksonReaderAdapter.wrapIfNecessary(xmlReader);
172174
_nameProcessor = nameProcessor;
173175
}
174176

@@ -557,14 +559,15 @@ private final String _collectUntilTag() throws XMLStreamException
557559
}
558560

559561
CharSequence chars = null;
560-
while (true) {
562+
main_loop:
563+
while (_xmlReader.hasNext()) {
561564
switch (_xmlReader.next()) {
562565
case XMLStreamConstants.START_ELEMENT:
563-
return (chars == null) ? "" : chars.toString();
566+
break main_loop;
564567

565568
case XMLStreamConstants.END_ELEMENT:
566569
case XMLStreamConstants.END_DOCUMENT:
567-
return (chars == null) ? "" : chars.toString();
570+
break main_loop;
568571

569572
// note: SPACE is ignorable (and seldom seen), not to be included
570573
case XMLStreamConstants.CHARACTERS:
@@ -586,6 +589,7 @@ private final String _collectUntilTag() throws XMLStreamException
586589
// any other type (proc instr, comment etc) is just ignored
587590
}
588591
}
592+
return (chars == null) ? "" : chars.toString();
589593
}
590594

591595
// Called to skip tokens until start/end tag (or end-of-document) found, but
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.fasterxml.jackson.dataformat.xml.util;
2+
3+
import javax.xml.stream.XMLStreamException;
4+
import javax.xml.stream.XMLStreamReader;
5+
6+
import org.codehaus.stax2.XMLStreamReader2;
7+
import org.codehaus.stax2.ri.Stax2ReaderAdapter;
8+
9+
import com.fasterxml.jackson.databind.util.ClassUtil;
10+
11+
/**
12+
* Refinement of {@link Stax2ReaderAdapter} to override certain methods,
13+
* to patch over flaws of JDK-provided default Stax implementation, SJSXP
14+
*
15+
* @since 2.17
16+
*/
17+
public class Stax2JacksonReaderAdapter
18+
extends Stax2ReaderAdapter
19+
{
20+
private final XMLStreamReader _delegate;
21+
22+
public Stax2JacksonReaderAdapter(XMLStreamReader sr) {
23+
super(sr);
24+
_delegate = sr;
25+
}
26+
27+
public static XMLStreamReader2 wrapIfNecessary(XMLStreamReader sr)
28+
{
29+
if (sr instanceof XMLStreamReader2) {
30+
return (XMLStreamReader2) sr;
31+
}
32+
return new Stax2JacksonReaderAdapter(sr);
33+
}
34+
35+
// 04-Dec-2023, tatu: Needed to catch exceptions from buggy SJSXP decoder...
36+
@Override
37+
public int next() throws XMLStreamException
38+
{
39+
try {
40+
return super.next();
41+
} catch (ArrayIndexOutOfBoundsException e) {
42+
// Use IllegalStateException since that is guaranteed to be translated
43+
// appropriately into Jackson type by caller:
44+
throw new IllegalStateException(
45+
"Internal processing error by `XMLStreamReader` of type "
46+
+ClassUtil.classNameOf(_delegate)+" when calling `next()` ("
47+
+"consider using Woodstox instead): "
48+
+e.getMessage(), e);
49+
}
50+
}
51+
}

src/test/java/com/fasterxml/jackson/dataformat/xml/deser/TestDeserialization.java

-123
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.fasterxml.jackson.dataformat.xml.fuzz;
2+
3+
import com.fasterxml.jackson.core.exc.StreamReadException;
4+
5+
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
6+
import com.fasterxml.jackson.dataformat.xml.XmlTestBase;
7+
8+
public class Fuzz618_64655_InvalidXMLTest extends XmlTestBase
9+
{
10+
private final XmlMapper MAPPER = newMapper();
11+
12+
public void testWithInvalidXml1() throws Exception {
13+
_testWithInvalidXml(1, "Unexpected end of input", // Woodstox
14+
"Internal processing error by `XMLStreamReader` of type" // SJSXP
15+
);
16+
}
17+
18+
public void testWithInvalidXml2() throws Exception {
19+
_testWithInvalidXml(2, "Unexpected character 'a'", // Woodstox
20+
"Internal processing error by `XMLInputFactory` of type " // SJSXP
21+
);
22+
}
23+
24+
public void testWithInvalidXml3() throws Exception {
25+
_testWithInvalidXml(3, "Unexpected EOF; was expecting a close tag", // Woodstox
26+
"XML document structures must start and end" // SJSXP
27+
);
28+
}
29+
30+
private void _testWithInvalidXml(int ix, String... errorToMatch) throws Exception
31+
{
32+
byte[] doc = readResource("/data/fuzz-618-"+ix+".xml");
33+
try {
34+
MAPPER.readTree(doc);
35+
} catch (StreamReadException e) {
36+
verifyException(e, errorToMatch);
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)