From 5cb768379d6a26de336117402959d960de5cf91b Mon Sep 17 00:00:00 2001 From: Dominik Stadler Date: Fri, 8 Sep 2023 16:02:13 +0000 Subject: [PATCH] Bug 66425: Avoid a NullPointerException found via oss-fuzz We try to avoid throwing NullPointerException, but it was possible to trigger one here with a specially crafted input-file Should fix https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=62128 git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1912199 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/hwpf/sprm/TableSprmUncompressor.java | 89 ++++++------------ .../converter/TestWordToConverterSuite.java | 8 +- .../converter/TestWordToTextConverter.java | 3 +- ...nimized-POIHWPFFuzzer-5050208641482752.doc | Bin 0 -> 56805 bytes test-data/spreadsheet/stress.xls | Bin 50688 -> 51200 bytes 5 files changed, 35 insertions(+), 65 deletions(-) create mode 100644 test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-5050208641482752.doc diff --git a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/TableSprmUncompressor.java b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/TableSprmUncompressor.java index 267f6b6875..e227403227 100644 --- a/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/TableSprmUncompressor.java +++ b/poi-scratchpad/src/main/java/org/apache/poi/hwpf/sprm/TableSprmUncompressor.java @@ -30,44 +30,39 @@ public final class TableSprmUncompressor extends SprmUncompressor { private static final Logger LOG = LogManager.getLogger(TableSprmUncompressor.class); - public TableSprmUncompressor() - { + public TableSprmUncompressor() { } - public static TableProperties uncompressTAP( SprmBuffer sprmBuffer ) - { + public static TableProperties uncompressTAP( SprmBuffer sprmBuffer ) { TableProperties tableProperties; + if (sprmBuffer == null) { + throw new IllegalArgumentException("Cannot process TableProperties with an empty SprmBuffer"); + } + SprmOperation sprmOperation = sprmBuffer.findSprm( (short) 0xd608 ); - if ( sprmOperation != null ) - { + if ( sprmOperation != null ) { byte[] grpprl = sprmOperation.getGrpprl(); int offset = sprmOperation.getGrpprlOffset(); short itcMac = grpprl[offset]; tableProperties = new TableProperties( itcMac ); - } - else - { + } else { LOG.atWarn().log("Some table rows didn't specify number of columns in SPRMs"); tableProperties = new TableProperties( (short) 1 ); } - for ( SprmIterator iterator = sprmBuffer.iterator(); iterator.hasNext(); ) - { + for ( SprmIterator iterator = sprmBuffer.iterator(); iterator.hasNext(); ) { SprmOperation sprm = iterator.next(); /* * TAPXs are actually PAPXs so we have to make sure we are only * trying to uncompress the right type of sprm. */ - if ( sprm.getType() == SprmOperation.TYPE_TAP ) - { - try - { + if ( sprm.getType() == SprmOperation.TYPE_TAP ) { + try { unCompressTAPOperation( tableProperties, sprm ); } - catch ( ArrayIndexOutOfBoundsException ex ) - { + catch ( ArrayIndexOutOfBoundsException ex ) { LOG.atError().withThrowable(ex).log("Unable to apply {}", sprm); } } @@ -82,29 +77,23 @@ public final class TableSprmUncompressor extends SprmUncompressor { * @param newTAP The TableProperties object to perform the operation on. * @param sprm The operation to perform. */ - static void unCompressTAPOperation (TableProperties newTAP, SprmOperation sprm) - { - switch (sprm.getOperation()) - { + static void unCompressTAPOperation (TableProperties newTAP, SprmOperation sprm) { + switch (sprm.getOperation()) { case 0: newTAP.setJc ((short) sprm.getOperand()); break; - case 0x01: - { + case 0x01: { short[] rgdxaCenter = newTAP.getRgdxaCenter (); short itcMac = newTAP.getItcMac (); int adjust = sprm.getOperand() - (rgdxaCenter[0] + newTAP.getDxaGapHalf ()); - for (int x = 0; x < itcMac; x++) - { + for (int x = 0; x < itcMac; x++) { rgdxaCenter[x] += (short) adjust; } break; } - case 0x02: - { + case 0x02: { short[] rgdxaCenter = newTAP.getRgdxaCenter (); - if (rgdxaCenter != null) - { + if (rgdxaCenter != null) { int adjust = newTAP.getDxaGapHalf () - sprm.getOperand(); rgdxaCenter[0] += (short) adjust; } @@ -117,8 +106,7 @@ public final class TableSprmUncompressor extends SprmUncompressor { case 0x04: newTAP.setFTableHeader (getFlag (sprm.getOperand())); break; - case 0x05: - { + case 0x05: { byte[] buf = sprm.getGrpprl(); int offset = sprm.getGrpprlOffset(); newTAP.setBrcTop(new BorderCode(buf, offset)); @@ -141,8 +129,7 @@ public final class TableSprmUncompressor extends SprmUncompressor { case 0x07: newTAP.setDyaRowHeight (sprm.getOperand()); break; - case 0x08: - { + case 0x08: { byte[] grpprl = sprm.getGrpprl(); int offset = sprm.getGrpprlOffset(); short itcMac = grpprl[offset]; @@ -154,8 +141,7 @@ public final class TableSprmUncompressor extends SprmUncompressor { newTAP.setRgtc (rgtc); // get the rgdxaCenters - for (int x = 0; x < itcMac; x++) - { + for (int x = 0; x < itcMac; x++) { rgdxaCenter[x] = LittleEndian.getShort (grpprl, offset + (1 + (x * 2))); } @@ -165,8 +151,7 @@ public final class TableSprmUncompressor extends SprmUncompressor { boolean hasTCs = startOfTCs < endOfSprm; - for (int x = 0; x < itcMac; x++) - { + for (int x = 0; x < itcMac; x++) { // Sometimes, the grpprl does not contain data at every offset. I have no idea why this happens. if(hasTCs && offset + (1 + ( (itcMac + 1) * 2) + (x * 20)) < grpprl.length) rgtc[x] = TableCellDescriptor.convertBytesToTC(grpprl, @@ -193,26 +178,19 @@ public final class TableSprmUncompressor extends SprmUncompressor { // for (int x = varParam[0]; x < varParam[1]; x++) // { // -// if ((varParam[2] & 0x08) > 0) -// { +// if ((varParam[2] & 0x08) > 0) { // short[] brcRight = rgtc[x].getBrcRight (); // brcRight[0] = LittleEndian.getShort (varParam, 6); // brcRight[1] = LittleEndian.getShort (varParam, 8); -// } -// else if ((varParam[2] & 0x04) > 0) -// { +// } else if ((varParam[2] & 0x04) > 0) { // short[] brcBottom = rgtc[x].getBrcBottom (); // brcBottom[0] = LittleEndian.getShort (varParam, 6); // brcBottom[1] = LittleEndian.getShort (varParam, 8); -// } -// else if ((varParam[2] & 0x02) > 0) -// { +// } else if ((varParam[2] & 0x02) > 0) { // short[] brcLeft = rgtc[x].getBrcLeft (); // brcLeft[0] = LittleEndian.getShort (varParam, 6); // brcLeft[1] = LittleEndian.getShort (varParam, 8); -// } -// else if ((varParam[2] & 0x01) > 0) -// { +// } else if ((varParam[2] & 0x01) > 0) { // short[] brcTop = rgtc[x].getBrcTop (); // brcTop[0] = LittleEndian.getShort (varParam, 6); // brcTop[1] = LittleEndian.getShort (varParam, 8); @@ -221,8 +199,7 @@ public final class TableSprmUncompressor extends SprmUncompressor { // break; // } break; - case 0x21: - { + case 0x21: { int param = sprm.getOperand(); int index = (param & 0xff000000) >> 24; int count = (param & 0x00ff0000) >> 16; @@ -231,15 +208,13 @@ public final class TableSprmUncompressor extends SprmUncompressor { short[] rgdxaCenter = new short[itcMac + count + 1]; TableCellDescriptor[] rgtc = new TableCellDescriptor[itcMac + count]; - if (index >= itcMac) - { + if (index >= itcMac) { index = itcMac; System.arraycopy(newTAP.getRgdxaCenter(), 0, rgdxaCenter, 0, itcMac + 1); System.arraycopy(newTAP.getRgtc(), 0, rgtc, 0, itcMac); } - else - { + else { //copy rgdxaCenter System.arraycopy(newTAP.getRgdxaCenter(), 0, rgdxaCenter, 0, index + 1); @@ -251,8 +226,7 @@ public final class TableSprmUncompressor extends SprmUncompressor { itcMac - index); } - for (int x = index; x < index + count; x++) - { + for (int x = index; x < index + count; x++) { rgtc[x] = new TableCellDescriptor(); rgdxaCenter[x] = (short)(rgdxaCenter[x - 1] + width); } @@ -311,7 +285,4 @@ public final class TableSprmUncompressor extends SprmUncompressor { break; } } - - - } 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 71f4327133..a2aac7b19f 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 @@ -38,8 +38,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -public class TestWordToConverterSuite -{ +public class TestWordToConverterSuite { /** * YK: a quick hack to exclude failing documents from the suite. */ @@ -60,7 +59,8 @@ public class TestWordToConverterSuite "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5418937293340672.doc", "TestHPSFWritingFunctionality.doc", "clusterfuzz-testcase-minimized-POIHWPFFuzzer-4947285593948160.doc", - "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5440721166139392.doc" + "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5440721166139392.doc", + "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5050208641482752.doc" ); public static Stream files() { @@ -139,6 +139,4 @@ public class TestWordToConverterSuite // no exceptions assertNotNull(stringWriter.toString()); } - - } 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 30e46e5d9f..2b2cfed94e 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 @@ -52,7 +52,8 @@ public class TestWordToTextConverter { "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5418937293340672.doc", "TestHPSFWritingFunctionality.doc", "clusterfuzz-testcase-minimized-POIHWPFFuzzer-4947285593948160.doc", - "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5440721166139392.doc" + "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5440721166139392.doc", + "clusterfuzz-testcase-minimized-POIHWPFFuzzer-5050208641482752.doc" ); /** diff --git a/test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-5050208641482752.doc b/test-data/document/clusterfuzz-testcase-minimized-POIHWPFFuzzer-5050208641482752.doc new file mode 100644 index 0000000000000000000000000000000000000000..4e03699c58d66d5bdd3758316538f3972dee5c82 GIT binary patch literal 56805 zcmeHw3v^Z0ng4emNp8Xo2?+!UAomeLK|>N=0clBign&FEFJGt$B#@Yp@CX*Qj!_XG z(^_iP+K#n!{b{v-+nH3?Ok3Nj+FGZhu94L`ES+(U)IRJ?TPNC%RS`13-`;26d+yD- zxi^79WSxE2@9g)tzy0lR@3YT2d+&SrrBO$}_MOsSN|O1c_~b-?u4LQ7J#b%Z{DmSN za5L;ge}6xN+W!T z$NNRf5dM?9MOu+@`e$`-`z^w)B1k4?O#Mx{u?Wl2ZDpd3<`t99*raPWhMuuay-gbW z%hANgkgo)RFY@AGlm1$SC(}vxQ$BWM=-IU#-GYgF30$doQ*$~ZRm1*VE_D)WxoM?3FI~f;8V|R+P5L#365z!Z^I9yr1i)ZJ(9G?Ewtp_b_74L2fYE^u?9zSjqwpkfU}wN}XCeX488DmM*8IiVucLdL_GkN^ zIFShfH_h210k6m7t&++Et)BTiLU}!MQH6JM?=9t_-k;Wnd+*MQ^nU1%9=JQJ>_Dgg z++LBfy@$QyfZ&0{c#JxWjrCxA7T3_NnkxZU0f&Y`2F8jTcqzJ2c9+CpJNE&;Bo_xQKh z=lFL6-EcQ}JvAPDEpPCWvNp%$HQ6jJpt)PNNGt3%*^074QYjZmJz|$jD;2dCE2h;dL8n_T%Vj#~ELUZdVn+f+%|}R9OP7=@ zS~^V|xHmjksvMP4h1``gQ(aBiEX$|kW?Md;2Je5Z9NK0c?V4rTq^U#ZSUEMls#sZ? zF7s*EEX$_flxe8Xv}lp04r7RkrAxb6x(SV^ovS#gw)oIBsj_N2&kEICR*B|vrcD+z z(fBhk)@7G@@}|g$I**Lphwo$idOf4|HJm6uv_M7!HB;m&1YeEdMg(JAT(iOl&GJFZ zCd%x9M>cuAN~1Q}H0pRtjiLk+HA;t6IcOAP?CG^GF{EQaI73@isFt$PDXlF!kJb=P zk=I&7)v(s_~B6_w@1NLwliH7~-aQv~JaS8xh=8<||NI zwY@$!up6Luy(K=SU2mz8P>)v|Q)(9_uv_+EG&Ai_tzc-UTqoDahmPYM<5$Z@#HShu znL^iYx}vs3*2}s=-Tf^?Y$1Y>3Q+1`aW-~Z(Yig*sBNU zg((c@bPrxnrf5}TMF+0a(VAVjBQSJ%ER^-}>-zX~efq4C;?k~N%}ctr?AhMZ+0C|l zP_k5A9<V9H3(8Wyh@Exi&@{Pf!RxiF=S*#A@Y zo@$H;>A0;{+y=mHPMq6=;I`4mZI0&lAh>P(fD(*zO9|d5xr*caY#gU7m*70b zQ!aQ4#CfX79<}fA`*8^dG*1=TjR-EX@l+(>3Ef}?`rkYKUy~KK6^i2#wizQ;6MEPU zxbr2?>=Nf;ob7R371<jPET67`Bb~U%+oN7H>oX>UO*MbxwnZI#_ zjC~i&xMv;77EhLOc5Usx)gVv>bh4+ErKQdx`=yF#IF8g+<7>& z(M8>ZqIUID^RqBIWMOm|4S9}HHCk;eA$5&7k1EAXT$gbavnNxHqnI)w67=g!JcVj{Py6EZp7t4fPOm3N)0>7?pvJ=X zkUmWg?r?mvYQbvq5boX7@3AUIpv6&@{sz_Sj<5YK!pEo{yct^Efn0l#cN==fb&gsu zkZ&nw)=xq=>P}Girj=nv{Up?*?u2??D}nxHOT&sB=wP;7y&@n@c!ul7Guyp#qU$^I zxJTcqMwZP$+2CG3w;CsQ!QT$|9*hM7 z9h@i^E(v*7J(mX`mTmXILQeL%H7oK^_dFEJIF4#+5K^zy%^GidzRXDTqP+)hf~H5X zW=ls6k|u{~*ytGa#1CM>AKD%(`~a5BF^%hfS;V8C$52=S2TV*+EboIga9DAQhaMe) zYfkEXGjl#J`BK$kZ|=+l8B%_f9py%usn#=cfgcjCuwK>WJoztDkz>U%jBTLJJ1Q>E zG49~7;|Ak}<+6P8Z?6OEkqT)_iM?^>hebjDds7I;-@%2$wcuCoJo(j+H zBLQPPJ^lTJSMpRoQdSI;;AfDk&_hM zygH|-zLQ1NZn+Yi?g7HX^T$R&QGD~N>rWzY^z#rkY?6_ZXP0j&( zUD8oBl#HE7aky0D9G+UbIkB1#q&q>-X#FSd__z0LTO3&d7?0e7xUwT3{LCrtPQ)F# z;>jnR;_gD+!OnBebBd#^iT;BA0v}o=OS$Na_c;0zf1X(TB!<)H0Tr!-PI1(I!YPhg zLpa4zn+T^kb{)bgZrg%Y@?`xDM+e3g%5TC`^X`7^jY&>%q4HM?%6v7QPH};opPTsT zS2|u87$@>x#S4?<_`Juy;-}?5zxI(#c{V&h|`LEts;}myS$$wT#mWf6&tzR0dd^KL=t5I(YnV3v7 zKl7^ln{rzhmG>}SI9422c@H!1VdSki&a}*1jzKJzpZ(HqRQ^UhejWT!NhQlW<@q=>Q^jGm5N8wEaxPHG_0#~nv3GKI^K_}DaUHnj|T4q z0hM!(V@}fv`upn(`cbM=C(`)`??h$WkLEcLMLxZw+;zLUy6(4#SG!k&iuZPi8`A2|<5qcPXWWX?5 zt*4TrNl;ZSA;wgd!{|T~JxU-67foVEAVy+G0Fu}d&?E5>NYspok*FB~5;Y@+OrmgP zj6~r`kSH8EWD-XUVKRBQW7SGWA_LV*MBl()yIms-*Wo#&q;>bsWtgGh!NTleEr$EJy>h8N_EfI><0%2&kA3)dul6-k zDJJ*cicO8rVN>HlY-&816+Lhen;H*ho!g5Wcki><)c7nmH9qT|C%F|?HtJ9&*+w+r zqY$#5L6Y*T>CYpWxLtlsO(h@UlYTpK=l-y_l|Zip=*HdMa$^K<9S8 zN$A|1L^GdxI2xGy(O|6XM}sK)(O|snfR($S?=rQCiGUL^mcbYu8%tv)1Q-XOadQ6t}0CaU@H#&Rxa~cIxts7o-V5*FJ4xbmCMK^A(ztMGrg_HLL$brT1`4; z&{9wN)`6hr&bbfT zo(1g0Qgf$?=`J;vuXqxs^s!ewc4F4c9tX~JRa4EthF#UP^D#Sj>8hG)W;U#)BK!rnU|wbX%q(F@y>lp0uVp55G2^25-wmVtWf>WBJ|! zjOBX^^rKUTZE1+0zkIDQ!JwA_9-Hb(4s zV?pe9V^5;0-z$v~`(7!CeXsN+t9s|87{NOyf#98!(pJ^!j^P_*+{N%1f6wLb8F=%} z8)Te~D-|aO))e8^R5UoWuIyJ+uGl-0mHl=a=Ef{3`EZ`3*lC#Cbn3b|52SV)=BDjU zSr?9-R$XGRNfPT4yM?E$i}R3a=OZ?0OG(3dq_oqBP2Ez~g@dD2m)M(<#JZRX+{ab9 z;iIsY@ zNc(5uDB?0WPZVpvu^CQd{KnRysjntovVE^b&)BIc=onLex^&6q82=wr@NeRcZRlgT zP~%0ioi08({i*Vs_A-2CY8!rsYNw)qFO~yLz!JT0kEfpslg zy4$)sEAxYvt14l&!g4y+hd2!mY}?5x$5oYy+RgljUJNeCjuP*Z<=F{a-rtr8<()Pmp|pKAqm78gS3! zdD&n2Y3sxHJ{&3qH~kqYfrS(Gg2?O7No4PSEDy;;&{tt|srDCe-?tx9`LFPE4fQ?9 z$8evOKiOUe`nMwfG0>@o{Y{I`G0Xpy<|VL?>}EbPb8mfp1RrSk402M$X1Iitkk6hToyuspu`hpplCKV<`p&`#f)yKi^s) zHq?^b^rue(f#T5OwM%Q)FI@tJLZMC5LaUapUADAt&DvG9>qDa}=gp|BoKaO#SrMw7 zHGkH;`4tso^6Oi6wS?MshnhkiZJq6*?ygW%XQ-vKxwEUg#p@Is+S=9Ctztv@>$wZJpaftst6}?+w*iNkgA%>uwEgZENXho*ya>)oxf{zhEr zX$`IG-nF@D4E|<9|Jj7P26i-UYeDXY+GUQlL-OKK-SU-7SJ$p`N?`WgS0Z749eD}0 zb%wTdZRckE`Jt@hP+GYmCn{yKF70ey4Jnl)q@yLYyX}S+R*oF3TYkmT!$bU|G(E?I zw%{Wfzd+D${wR$)#IeRTA0G)E2dkKDt2X4|$rbh7^upydoMy0XIY;B_bk5QEOon}7 z*f6HI333%T@7a7M!#M^14NA?`Fy&Em9p*2Eo3?AtJPEADW?yc#56M!s*>{&*kJifE zlt5!ltvDVGW-4ZO%-Ff+1+_h-hROuvku}!g2HKy=)%JZJ&ktUYDW7&G`I*Lg<%R=r z^dSrSfQ3wR$J32O`5Xh;cQP%X=@(cVOgz|P*@dmvJ0OS6@xP&r7m&tPG>rOB0Zr4& z?3Zje?!rm#{~Q;{=E?Mbjv?&-697Io;gJH{-bE$@As`Gy06ub*0p|kbX{u$P4|^KG zHwqU3+}ltNFb$twQjaUg*c<~52RzrrBmiAZ*KHx?)TRg{eja-9mAE4qLYr}G;GuW0 zaUbE^q^;^O1AV9z?-)LH!ks?q4rkus__9UQaB_g&fWlMnaB>vR2+8#~AdDMQcys|z zz~jkxo%McdX0ll%wKY7RWf3BVdn}8gxj;@#ey9I!;ZtZyQVl}VfZFT=wyEn59EuXc zu_aw<{3L}s>tY+1N^Ke{=J_cnk(oB>Yg`+$X5Br%vcEk%Cn0IK_$s<)B)+q<0 zP7}tQZoC^L-{h(|cA!-dT#dwG4&!+64JCh7z!Ooq7s2KnA%4ULeWQgrv>~q;M@G^| zCDWlpyrOwG0i26)!8_fYlVHx#*P%#dt4--X6y$}UVEF{cvB~1;<5;m-a{4%?WM}_A zc>8zZkjVY@cg^*3?z#tR$n;f;Q;}3#_o>!>RY+-e+N%OlFn8J?0fC`z-DmoTk?(Q8 z-qP;{Esn6iz)t(saNYvw*I6RBDfd2g_1BW!Y+eL^Obc@hZ+vo_azEuM1c!QO{yxe; z_N(h+iM-9XL7(%H?*r=kc@=Z7a-&{q|2}gh?&VK94}e@kRL=43bR0Xr<#(WKFFVy+ z{%P(`ZwDqE59i{scy95`3;_5D64rl;AGP`w7Q$*1;*MdG0f|JXExM1eGFfGexy?m zykZ@$hw~J^FCh;DQw$6_4?+7pIy`GY&Ex#=Sv(y-vofpy`*tpPQ|`6`{_u5=XH_Vd zxA&r_beQhj*1QYzz^1N_rp{`I9-r$kiOG>!&5c}>8F=l-2_BRct4=p{#<-JH->CwE z*6?KP`#D>#r}}8{~RLP zOdin!0q}7#=An7~6)8Cx}xeQXonG?0}G_tY~4Mjyf-Nq}gp6(*u z^8ODc@Yg46t{I8Z5NHCnVh*tlXa#;~XZkw{w_9`DO>5goeADJmeB-&<9+SBnh4Z!I zNf_L{OUgXUw_=Esi}5bwlOpZS<1sB$p`70!bY+<*aD)ek$v>Lh#E7;Q+fm) zTKXqwYofL$X=^5r?ZwGv`utux)Umxgj-V_aq^V%IvC8G-&R6D%G1ye*A&-m5;|sV+ zB=IQgJ!0{>Zd?y*lw?M~>$_y!Dd6*N6eL+xkT&32pdHu_><4~~7cAefmweFfS!?&K zw0r989<;-tCZ`~w-Lh?7OfF&)TTM~DubXo$+nKkqmgZMSZO*2&a_)P}Ye}qRN(zaU zOo7DI$;;z-&hjwYxUHMzCA2gpX!s;c_?r+gdY#cbOB}_;g72 z-xQL625$YUko?LMmZ&!@`+=u@VYx6XEYIYIWkFt8#*GNe!}(#kU}RVtfU?4{tOE`K z$AE8+3d@&^!>DFh?gDNY8e9>OjP%hClX z56G=SUf@4!!?I*iSndWs0N!03mgXg4`LCs6xuQNSH(d-KSB9m070Or>mh}x`dGeC5 z{BT29He42#e+AySJS-cp3Cjb(FM$7P1TRftc@db{9F|ZE!ncNHcUxG#0zBFtmLCBx z0y8>L{&vU)_}cEUytyYV)i;FYWuWfHu#CAWEWZKv-5i#0V{ZHb@SZ0kjoA^oBPSyF z1|sr9;FjEo+>{rQ9|R-v4+RnVZBaySEsjY0n22mFiO7M{h#Z>`k=H^I=m0u!$|pbb z#A|Hoj4=*RzI73KZW*vVB9m9bUX8LYiO6HXt7{`tvJPdgkI0ucMC2#H^&2Dd1z`N8 z5ox$ABKHAPuZYN1z?3T^lD{b;4_y_JzcfZ<|CWe+7WgjUZH`D)OGEKLRs7QTZxRUdbT=xG05TT&f$PUbB|JVVuarim zVM0`XGchVtCq?Dwz{Tf8W!mJZYzOWH&W(b`)TsQ+^r-y53!^f7MpPD6M&+HVsC)?Y z%!8hywYBl&= z8!`iN>h>6xg^qDla#qzAfg? zi}JTdWz)8(+zae%i%R>oQTa-HROWY}d|=h~sEq84%7U(_Oy3oiZvxA9N977&FR!t2fUW%;wbK7apwIa<3{p62>l8dVVNbh{YzzKg6Z zkJpov?aks+SsE-9h?yR`oc3LKM`ZiGG^+H)4yD`oKGfobqZmy%{!XN4MJOJEwFt^^ zFBNxuUWo5t{EOQ*wf?*bam$8GVVPR3&cD*$4SDOo@aD1vaxxUzVcCNhc?^r-h3kLp z##9Zf31a*^m!2+uDN5QO@OrYv=TSx#Gcpiw5-wp9;ptD@Q^PgMo$x36+KR|;ypDtM zJNV9=bQ)9}K}k7rT}WM0f;Omi{!MB{`|K&IJ)licWeP~uXWyQq={bIFC|gDl2?pimeB5 z%}Gh1Xch;r@z}MX&qz2aVpisOYX+-s+ljy)|7*!wZZD@EOU1A&w2yyq>Z#y3uFTE2 zI^xI`Ju3U->S`|3jQDOG6Iv*%pvPO#51Q0C!%?6cy155o?Fi{a?bD?&wCwg?c(_`I zd%?Nn*#qEzW-!hLp}9fyvmoMwxOW9{uL@$%YY=N6gE)I4sDAmljsyyTLSPh71QY|< zgNjx1LG0EI;$Q26*i{=u3k9+F{G(V}tt#kVJ~|0tPY3qO#ygeagbIK3_OHJ4{+iap z$37pB=~JJ06}?Ao$Kjoq@dp6rd=Ow4JOucGUVz(IUIeJduK=9O{2JiY>OFu{qtPJ3 z4Q?UW-1b@nn@YG6Hm6mW!Nv+}X@<>-TL=?LeGKCvT2gtSOm%K zVXaLq9XyF>&zANU-lGSkv*#xttGjk>Z|W#-?&{WQ)6uE2XpV#JxE&4QHGn)+@u$Cj z^LdYQA6@;Qx9xv1!A)7BYO!ez{WQkbKKJTMz?Fb20apU91Y8NY5^yEpO2CzXD*;ym zt^`~OxDs$B;7Y)ifGYu401zZhW12h7R-wb;T z&~0sRqE%3; zRjUbc`ed}JZNzI7IEnWXF%d{==}VKQeQNq>`p^gaVpKY3_+UGevSE_%Kfn2XGqZ;U z2F@D>h73b#OI(0oQ2TkZXqWON5_l@%`2nIbv7JI4!61kJso~u z_bG^i!Ykxt#QCt(Mu4xh)>k0lWFx4OH#ExTMTo$i{TKj04bbhx`MhdO$^7MIWB?h5quS!x5puA<)dc6V?+ zfIsL-bp>3lUUzE1-;wGKbhoFL*VL9edwo3}UG9aQEvHTVy`7P57QIo>?P{~McXVp+ z$}SieYNPI z+HAE{L;fB#ro5nm3F0MvqCir>EJwlQ|725orv3BHPg4Io`i|+#qTwUDY_5`Mr5|^$ zl4-Scp)%5H!$K7XF<`-UdZUnKl3qnLWFR?7_ZbbD3zcqJqpx$)_LKC~F|Zaj`rbmC zDW);w!YNKOMQ<+_&MwS_!CE*6KqkID963!%dRZ_{U$U%~U9Feayj4RtDd{)AI8=w} zH>*AS?t5AJ7&oFXkA-z4cr8haCzr2Tr35lf$3*g)j_ZkK*eWHF%3*6xGOjSO74xSw z(JjP+mClXRLz?JYzly$$>{jU;N#{0@k>NPWN?s1f<)om)%sI@Q!^}ArF-Iz~T-P1B z*W+?Fq9dGhgmaE?&as#|(#Q~V+@X$ibm$Lr?7}-1!8sy0#}ekSUA&=hxgN^8~UD9NQqU`xtu?*?rh+^%GSY#B4ui6;D*p7nmL3 zoCgG_{trVeoI9Xh|5Wvy!OX=uU4pZPInN^7!m%v^ z+sfE;$hOY0+RRg%M23vL|2>mbG0{RU3u#ZH$T$eJU1Es1t8W++yDRo delta 2632 zcmZ9MTTE0}6o&Vn84$T0RO%R~JBY0yj#NNkz{6GKCX6r$%3Y9Zt0Q8ocC?lXUfML} zL9?HXG1byU5lwZDq$+}?*!D%!J~bh2W7?OdkEDIErqFfH{B$^n$?Rl*d#&$ZGy4n! z7wiK=_6r$tT?~H-jCI~E^w^ip&Cky-yu0ae?xOEh&~Y$oRavE88qLa2^z1uw>_Fe) zqvu%y+kaV}i5-dvI2ij_TM;ML*nUdVveKWf*1oG6aztd6HRQf32Wwq2;N81wnyp;I zSS)D{r88C(N%jiHeoWG4y{-JTzu^@W3X>zzaOB9IQ4i0Rmq*>)BfqCLPyR(~ zzD&C1=38V9t*Q*%^6&!rC9Q?>47cieodETgqd_TBOD zZSp#;Pe*icReSED1r{=@(RD2`V*$-b8IF zq=WZ`vJY%4*ll9_!OFm9?hExTux+NCZI+yJBj;_@mZLxOflv;BRe*I6I|x<@_Q?aG z`oXG9IaQXNY9r?mYOB%z8##Nywu8kz6v|<+8nFC_LhS>qHRaS=a=b>)5!8Cof03N; zfb9UAAoea;9oV0ga}=!Jlv8iX@fkVCQ0qf~>9|migEfHl5_=D9C)lNNp$5PjO*xI0 zoF*gZ1ZtbmKTFP&V7tJ?BcZ$xwi~Sakx);8HJfspEjcYl&IhP%LH{*!_Jg&8JtOuZ zSR2?2$~g_zZpvx5;4?^IvKIJc+db2WB_qUH||9