diff --git a/backport-CVE-2022-40152.patch b/backport-CVE-2022-40152.patch new file mode 100644 index 0000000000000000000000000000000000000000..9cb6afd4e29eb3e291e9d4f9efaebd7c26bf1f84 --- /dev/null +++ b/backport-CVE-2022-40152.patch @@ -0,0 +1,219 @@ +From 7e93907e9c98270e76e20d55c4d35bd600edbb20 Mon Sep 17 00:00:00 2001 +From: PJ Fanning +Date: Mon, 24 Oct 2022 03:01:56 +0100 +Subject: [PATCH] add recursion limit of 500 for DTD parsing (#159) + +Origin: +https://github.com/FasterXML/woodstox/commit/7e93907e9c98270e76e20d55c4d35bd600edbb20 +--- + .../java/com/ctc/wstx/dtd/FullDTDReader.java | 35 +++++++-- + src/test/java/wstxtest/BaseWstxTest.java | 23 ++++++ + .../java/wstxtest/fuzz/Fuzz_DTDReadTest.java | 76 +++++++++++++++++++ + ...se-modified-XmlFuzzer-5219006592450560.txt | 1 + + 4 files changed, 128 insertions(+), 7 deletions(-) + create mode 100644 src/test/java/wstxtest/fuzz/Fuzz_DTDReadTest.java + create mode 100644 src/test/resources/fuzz/clusterfuzz-testcase-modified-XmlFuzzer-5219006592450560.txt + +diff --git a/src/main/java/com/ctc/wstx/dtd/FullDTDReader.java b/src/main/java/com/ctc/wstx/dtd/FullDTDReader.java +index f9a04a4..407ab54 100644 +--- a/src/main/java/com/ctc/wstx/dtd/FullDTDReader.java ++++ b/src/main/java/com/ctc/wstx/dtd/FullDTDReader.java +@@ -74,6 +74,9 @@ public class FullDTDReader + + final static Boolean ENTITY_EXP_PE = Boolean.TRUE; + ++ final static int DEFAULT_DTD_RECURSION_DEPTH_LIMIT = 500; ++ static int DTD_RECURSION_DEPTH_LIMIT = DEFAULT_DTD_RECURSION_DEPTH_LIMIT; ++ + /* + /////////////////////////////////////////////////////////// + // Configuration +@@ -327,6 +330,24 @@ public class FullDTDReader + + transient TextBuffer mTextBuffer = null; + ++ /** ++ * Sets the limit on how many times the code will recurse through DTD data. ++ * The default is 500. ++ * @param limit new limit on how many times the code will recurse through DTD data ++ */ ++ public static void setDtdRecursionDepthLimit(final int limit) { ++ DTD_RECURSION_DEPTH_LIMIT = limit; ++ } ++ ++ /** ++ * Gets the limit on how many times the code will recurse through DTD data. ++ * The default is 500. ++ * @return limit on how many times the code will recurse through DTD data ++ */ ++ public static int getDtdRecursionDepthLimit() { ++ return DTD_RECURSION_DEPTH_LIMIT; ++ } ++ + /* + /////////////////////////////////////////////////////////// + // Life-cycle +@@ -2266,7 +2287,7 @@ public class FullDTDReader + vldContent = XMLValidator.CONTENT_ALLOW_ANY_TEXT; // checked against DTD + } else { + --mInputPtr; // let's push it back... +- ContentSpec spec = readContentSpec(elemName, true, mCfgFullyValidating); ++ ContentSpec spec = readContentSpec(elemName, mCfgFullyValidating, 0); + val = spec.getSimpleValidator(); + if (val == null) { + val = new DFAValidator(DFAState.constructDFA(spec)); +@@ -3044,13 +3065,13 @@ public class FullDTDReader + return val; + } + +- /** +- * @param mainLevel Whether this is the main-level content specification or nested +- */ +- private ContentSpec readContentSpec(PrefixedName elemName, boolean mainLevel, +- boolean construct) ++ private ContentSpec readContentSpec(final PrefixedName elemName, final boolean construct, final int recursionDepth) + throws XMLStreamException + { ++ if (recursionDepth > DTD_RECURSION_DEPTH_LIMIT) { ++ throw new XMLStreamException("FullDTDReader has reached recursion depth limit of " + DTD_RECURSION_DEPTH_LIMIT); ++ } ++ + ArrayList subSpecs = new ArrayList(); + boolean isChoice = false; // default to sequence + boolean choiceSet = false; +@@ -3082,7 +3103,7 @@ public class FullDTDReader + } + } + if (c == '(') { +- ContentSpec cs = readContentSpec(elemName, false, construct); ++ ContentSpec cs = readContentSpec(elemName, construct, recursionDepth + 1); + subSpecs.add(cs); + continue; + } +diff --git a/src/test/java/wstxtest/BaseWstxTest.java b/src/test/java/wstxtest/BaseWstxTest.java +index 8bcb596..a3c29fa 100644 +--- a/src/test/java/wstxtest/BaseWstxTest.java ++++ b/src/test/java/wstxtest/BaseWstxTest.java +@@ -532,6 +532,29 @@ public abstract class BaseWstxTest + } + return bytes.toByteArray(); + } ++ ++ protected byte[] readResource(String ref) ++ { ++ ByteArrayOutputStream bytes = new ByteArrayOutputStream(); ++ final byte[] buf = new byte[4000]; ++ ++ InputStream in = getClass().getResourceAsStream(ref); ++ if (in != null) { ++ try { ++ int len; ++ while ((len = in.read(buf)) > 0) { ++ bytes.write(buf, 0, len); ++ } ++ in.close(); ++ } catch (IOException e) { ++ throw new RuntimeException("Failed to read resource '"+ref+"': "+e); ++ } ++ } ++ if (bytes.size() == 0) { ++ throw new IllegalArgumentException("Failed to read resource '"+ref+"': empty resource?"); ++ } ++ return bytes.toByteArray(); ++ } + + /* + ////////////////////////////////////////////////// +diff --git a/src/test/java/wstxtest/fuzz/Fuzz_DTDReadTest.java b/src/test/java/wstxtest/fuzz/Fuzz_DTDReadTest.java +new file mode 100644 +index 0000000..d784125 +--- /dev/null ++++ b/src/test/java/wstxtest/fuzz/Fuzz_DTDReadTest.java +@@ -0,0 +1,76 @@ ++package wstxtest.fuzz; ++ ++import com.ctc.wstx.dtd.FullDTDReader; ++import com.ctc.wstx.exc.WstxLazyException; ++import com.ctc.wstx.stax.WstxInputFactory; ++import org.codehaus.stax2.io.Stax2ByteArraySource; ++import wstxtest.stream.BaseStreamTest; ++ ++import javax.xml.stream.XMLStreamReader; ++import java.io.ByteArrayInputStream; ++import java.io.InputStreamReader; ++import java.io.Reader; ++ ++public class Fuzz_DTDReadTest extends BaseStreamTest ++{ ++ private final byte[] DOC = readResource("/fuzz/clusterfuzz-testcase-modified-XmlFuzzer-5219006592450560.txt"); ++ ++ private final WstxInputFactory STAX_F = getWstxInputFactory(); ++ ++ public void testIssueInputStream() throws Exception ++ { ++ XMLStreamReader sr = STAX_F.createXMLStreamReader(new ByteArrayInputStream(DOC)); ++ try { ++ streamThrough(sr); ++ fail("Should not pass"); ++ } catch (WstxLazyException e) { ++ verifyException(e, "FullDTDReader has reached recursion depth limit of 500"); ++ } ++ sr.close(); ++ } ++ ++ public void testIssueInputStreamHigherRecursionLimit() throws Exception ++ { ++ final int defaultLimit = FullDTDReader.getDtdRecursionDepthLimit(); ++ XMLStreamReader sr = STAX_F.createXMLStreamReader(new ByteArrayInputStream(DOC)); ++ try { ++ FullDTDReader.setDtdRecursionDepthLimit(1000); ++ streamThrough(sr); ++ fail("Should not pass"); ++ } catch (WstxLazyException e) { ++ verifyException(e, "FullDTDReader has reached recursion depth limit of 1000"); ++ } finally { ++ FullDTDReader.setDtdRecursionDepthLimit(defaultLimit); ++ } ++ sr.close(); ++ } ++ ++ public void testIssueReader() throws Exception ++ { ++ Reader r = new InputStreamReader(new ByteArrayInputStream(DOC), ++ "UTF-8"); ++ XMLStreamReader sr = STAX_F.createXMLStreamReader(r); ++ try { ++ streamThrough(sr); ++ fail("Should not pass"); ++ } catch (WstxLazyException e) { ++ verifyException(e, "FullDTDReader has reached recursion depth limit of 500"); ++ } ++ sr.close(); ++ } ++ ++ public void testIssueStax2ByteArray() throws Exception ++ { ++ // Then "native" Byte array ++ Stax2ByteArraySource src = new Stax2ByteArraySource(DOC, 0, DOC.length); ++ XMLStreamReader sr = STAX_F.createXMLStreamReader(src); ++ try { ++ streamThrough(sr); ++ fail("Should not pass"); ++ } catch (WstxLazyException e) { ++ verifyException(e, "FullDTDReader has reached recursion depth limit of 500"); ++ } ++ sr.close(); ++ } ++} ++ +diff --git a/src/test/resources/fuzz/clusterfuzz-testcase-modified-XmlFuzzer-5219006592450560.txt b/src/test/resources/fuzz/clusterfuzz-testcase-modified-XmlFuzzer-5219006592450560.txt +new file mode 100644 +index 0000000..1fb049b +--- /dev/null ++++ b/src/test/resources/fuzz/clusterfuzz-testcase-modified-XmlFuzzer-5219006592450560.txt +@@ -0,0 +1 @@ ++ - 6.2.8-2 +- Fix CVE-2022-40152 + * Fri Nov 10 2023 yaoxin - 6.2.8-1 - Upgrade to 6.2.8