From 2e8afc0c016cee39f56eb5e88ace66ed7f09c7b5 Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Wed, 9 Aug 2023 05:45:21 +0000 Subject: [PATCH] Bug 66425: Avoid a StackOverflowException found via oss-fuzz We try to avoid causing StackOverflow, but it was possible to trigger one here with a specially crafted input-file. This puts a limit on the number of nested properties in place and logs a warning when the StyleSheet is not fully parsed. Should fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=61252 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1911563 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/hwpf/model/StyleSheet.java | 32 ++++++++++++++---- .../converter/TestWordToConverterSuite.java | 3 +- .../converter/TestWordToTextConverter.java | 3 +- ...nimized-POIHWPFFuzzer-4947285593948160.doc | Bin 0 -> 56832 bytes test-data/spreadsheet/stress.xls | Bin 62464 -> 62464 bytes 5 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-4947285593948160.doc diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/StyleSheet.java b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/StyleSheet.java index 348c23677f..e19332aa56 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/StyleSheet.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/model/StyleSheet.java @@ -20,6 +20,8 @@ package org.apache.poi.hwpf.model; import java.io.IOException; import java.io.OutputStream; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.poi.hwpf.sprm.CharacterSprmUncompressor; import org.apache.poi.hwpf.sprm.ParagraphSprmUncompressor; import org.apache.poi.hwpf.usermodel.CharacterProperties; @@ -39,6 +41,7 @@ import org.apache.poi.util.LittleEndianConsts; */ @Internal public final class StyleSheet { + private static final Logger LOG = LogManager.getLogger(StyleSheet.class); public static final int NIL_STYLE = 4095; // private static final int PAP_TYPE = 1; @@ -46,6 +49,9 @@ public final class StyleSheet { // private static final int SEP_TYPE = 4; // private static final int TAP_TYPE = 5; + private static final int MAX_PAPX_NESTING = 1000; + private static final int MAX_CHPX_NESTING = 1000; + @Deprecated private static final ParagraphProperties NIL_PAP = new ParagraphProperties(); @Deprecated @@ -114,8 +120,8 @@ public final class StyleSheet { } for (int x = 0; x < _styleDescriptions.length; x++) { if (_styleDescriptions[x] != null) { - createPap(x); - createChp(x); + createPap(x, 0); + createChp(x, 0); } } } @@ -203,7 +209,14 @@ public final class StyleSheet { * ParagraphProperties from (and also place the finished PAP in) */ @Deprecated - private void createPap(int istd) { + private void createPap(int istd, int nesting) { + if (nesting > MAX_PAPX_NESTING) { + LOG.warn("Encountered too deep nesting, cannot fully process stylesheet at " + istd + + " with more than " + MAX_PAPX_NESTING + " nested ParagraphProperties." + + " Some data could not be parsed."); + return; + } + StyleDescription sd = _styleDescriptions[istd]; if (sd == null) { throw new IllegalStateException("Cannot create Pap, empty styleDescription, had : " + _styleDescriptions.length + " descriptions"); @@ -227,7 +240,7 @@ public final class StyleSheet { throw new IllegalStateException("Pap style " + istd + " claimed to have itself as its parent, which isn't allowed"); } // Create the parent style - createPap(baseIndex); + createPap(baseIndex, nesting+1); parentPAP = styleDescription.getPAP(); } @@ -253,7 +266,14 @@ public final class StyleSheet { * CharacterProperties object from. */ @Deprecated - private void createChp(int istd) { + private void createChp(int istd, int nesting) { + if (nesting > MAX_CHPX_NESTING) { + LOG.warn("Encountered too deep nesting, cannot fully process stylesheet at " + istd + + " with more than " + MAX_CHPX_NESTING + " nested CharacterProperties." + + " Some data could not be parsed."); + return; + } + StyleDescription sd = _styleDescriptions[istd]; if (sd == null) { throw new IllegalStateException("Cannot create Chp, empty styleDescription, had : " + _styleDescriptions.length + " descriptions"); @@ -282,7 +302,7 @@ public final class StyleSheet { parentCHP = styleDescription.getCHP(); if (parentCHP == null) { - createChp(baseIndex); + createChp(baseIndex, nesting + 1); parentCHP = styleDescription.getCHP(); } if (parentCHP == null) { diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToConverterSuite.java b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToConverterSuite.java index 9f97d63309..cafc6c3dfa 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToConverterSuite.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToConverterSuite.java @@ -58,7 +58,8 @@ public class TestWordToConverterSuite // Corrupt files "Fuzzed.doc", "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5418937293340672.doc", - "TestHPSFWritingFunctionality.doc" + "TestHPSFWritingFunctionality.doc", + "clusterfuzz-testcase-minimized-POIHWPFFuzzer-4947285593948160.doc" ); public static Stream files() { diff --git a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToTextConverter.java b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToTextConverter.java index 0d084157c9..4bb810735f 100644 --- a/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToTextConverter.java +++ b/poi-scratchpad/src/test/java/org/apache/poi/hwpf/converter/TestWordToTextConverter.java @@ -50,7 +50,8 @@ public class TestWordToTextConverter { "TestRobert_Flaherty.doc", // Corrupt files "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5418937293340672.doc", - "TestHPSFWritingFunctionality.doc" + "TestHPSFWritingFunctionality.doc", + "clusterfuzz-testcase-minimized-POIHWPFFuzzer-4947285593948160.doc" ); /** diff --git a/test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-4947285593948160.doc b/test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-4947285593948160.doc new file mode 100644 index 0000000000000000000000000000000000000000..ce753c01b23f55174d635fed01214ec18bd6e70d GIT binary patch literal 56832 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 PPo}M_b}RjpgTVg+J44>) literal 0 HcmV?d00001 diff --git a/test-data/spreadsheet/stress.xls b/test-data/spreadsheet/stress.xls index cf344f0b847ecad437b31bced212587486dde191..b9ac3702aafc138804d43d09ff9b0f1d986323ad 100644 GIT binary patch delta 1406 zcmZWpZERCz6n^e)*LD43j!p&+cS)RR+{)NT*Tyks1+ok_X84dXKJ2<8!_m&IGs}YO z(l|9FqRDHFw7q2b%da@%HAa+}XpA${#Hl}sA0&zynMR_&)BuXly=^aPY?D6cyyv{n zdCqy?x6{j(>1E5gVXLj<vCo(p}knbgu75UZCV!$Yr(Da{pPOEu?nAu znOd7?(ouE7?!^f^>zbb)U~|Z+U8#D%Mw%061C{d2pNaPZdrRK60Ee}e!L^F)yj^ST z8>^JM#HPpUlbsLuZre6KIT;eTNgRzT(b%98KVq0;35Jf} z#DHYSzdpc4y~j2H6F?H{8h{Nvjl@PE#UXBP0=lh?v;fc9fUADsdMPj+08W+B57Ku4 z_uUT!$+V{(h^zqiJpdf30B(c;hZneK3*fH=-sl9*uV%q67Q6%aWIOP6HE?YQV81g7 zbaH5WYk#z&Q$R+y_)s*uZ{Z zhm&IZ*#L!oNPLy}%mLtWH@iLv{7LL0HhX{>V!WHi$28s~UMDUQ{f7XB9mR=##Mg;0 z60Z;!i5Cw8AC&`7Jp#mT19lGp?JGH%h*mPv^jWzNTuH`5AjMgg!3+0|!p?9*2xW%Xu3wAlY94|? zoi(Ym2K7O6lTLjoPn|WX)go%ONVHlcS~Zzf+^efDP^*UO0<~&TUqlC<mn-m&?Ii!-GK&o^US5{U@@utkTRXh z(00E$9`%ncfwp0seOKMRIl(qxgf^de=CgoPSI#s$y#?LH1!c|dv7gy3>o&xjKijgo zX(xI7cgb{BEYCXTfmP1ailP#`Dc>1c+mzq_f=0{7h%yMM?ki!7aM}bY)B3)js54Ey zViL^v!rm$HLpEfU!_y(KuL5@ED4wp^4>Lbht%N?Wf^|b7UJV<=5Sb}Y5tuOxrsl)1 zbK%@O(r&L&Lk)K8V@MT3cs}^vgN7I^FNP-z;13y!E`on#=%Ucb(5Fk(NQRazg>hN% zSlA={YZ;u7!J3s&C}Z2}U|ue?)@uM6`%`#ScwiN54MOASPvBpd2q%YNzi^B2rtp|> z_-eQ>j0sCL(YL}{;V;5H!so(k!iQ^QE)S9ouz474YJ{rc+RS=qXS^mfGN~bsd<`K3 zBM!oaAZ|=MKU~a z=j9_xGf@`R)D6@nboyE)kydFM$V}*e5kvb)t0Z_+C4p4NXJk4D$#f2q=}gP$oiIcD zqS0#V)%rS>#I^B4yN>D1WtKU)KWf6MNN^5wsItSH&i+mn<*5k)rAsfVuVtA%5675& hsnOxq-=MQko4*P5ci`|{r#JOMk(2jj(mM3W`#--=>0ST;