From c28d114b79aa09566b9f605a988cad36b5915213 Mon Sep 17 00:00:00 2001 From: ricflams Date: Wed, 1 Oct 2025 01:25:36 +0200 Subject: [PATCH] Guard against circular references in XRef tables/streams - Detect and prevent an xref table/stream at a certain offset from being read twice; malformed xref tables with circular references could otherwise cause the table-reading to loop forever. - Another approach could be to prevent TryReadTableAtOffset from changing the bytes' CurrentOffset to the lastObjPosition in its attempt to read a table (eg restore CurrentOffset after the attempt to read a Table) so the outer bytes-loop could continue its search through the entire bytes unaffected. --- .../Integration/CrossReferenceParserTests.cs | 12 ++++++++++ .../B17-2000-transportation-fuels.pdf | Bin 0 -> 34368 bytes .../Parser/FileStructure/XrefBruteForcer.cs | 22 ++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 src/UglyToad.PdfPig.Tests/Integration/SpecificTestDocuments/B17-2000-transportation-fuels.pdf diff --git a/src/UglyToad.PdfPig.Tests/Integration/CrossReferenceParserTests.cs b/src/UglyToad.PdfPig.Tests/Integration/CrossReferenceParserTests.cs index beab121b..71165e59 100644 --- a/src/UglyToad.PdfPig.Tests/Integration/CrossReferenceParserTests.cs +++ b/src/UglyToad.PdfPig.Tests/Integration/CrossReferenceParserTests.cs @@ -9,5 +9,17 @@ using var document = PdfDocument.Open(path); Assert.Equal(3, document.NumberOfPages); } + + [Fact] + public void CanReadDocumentWithCircularXRef() + { + string path = IntegrationHelpers.GetSpecificTestDocumentPath("B17-2000-transportation-fuels.pdf"); + + // If parser can't deal with xrefs that have circular references then + // opening the document will loop forever + using var document = PdfDocument.Open(path); + + Assert.Equal(1, document.NumberOfPages); + } } } diff --git a/src/UglyToad.PdfPig.Tests/Integration/SpecificTestDocuments/B17-2000-transportation-fuels.pdf b/src/UglyToad.PdfPig.Tests/Integration/SpecificTestDocuments/B17-2000-transportation-fuels.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fb1315f26bf2b6fcbef3f6584651f278f7a38d9f GIT binary patch literal 34368 zcmb@t1wdU(vM#&{?ry;?xVu}h;O-D)(2W8`22a&|ETizDs?G;+4Iw|i^k45WF>$Hv0S!@|nO%fiFP&BnsW z!b#1-LJcO9w>SO2P$)Uso4S|)ooJq(0rCJ#fC<0}U=IL+iy6QfPzE3cNC4cyy+DA? z?-m|ElAQ+W%r3FbGaSuxp zu1l$Mk^TljNQha|-p(1^CCN(4{bEOENj6g6KPv2`tS?xDyEsT$Uo5HuCf8ClwgQ^G zjF7Pfk$@8STS>bINyZZNAdt2r6jfgE1cH1S{-S7m++ zbhR`Asz`}}`KlQigTN$zINTqm0Fko%)oL${|7JBwOB-jP6SJfZ*eY*<%P5dD*jH?k5!_P`^{vYnxndvfI-W&40AchdWo0)wf$ssrkjH(Wsa?^a z;vQE&mlK)vw;2eVnSZ!F)s?8c*uhb^sl_`Ekr*95)Zlc{lBiN3k~!Ghv%I{V(NsJV z_EoC&yHj;J?tI*9w_m|1KCOx^wXP{lJ&`x3-;yv$;_`zGYb zFYpu;*X@RX@N4Cof;}j)A)u!}beR|&M401=GQ21yZo|KuuP;xNa3@dzlwrhOHQQBs zFs_E_Cc~D8M%t64hA#;;`b^zQ(k%-Q4&=M&D!g~j!9ba3@f;g(G!l}A9nL^X+*^yg zSLggXw!XK3du*VI``qB|c7D_($n-uou;# zuo+IO%Mjf7+3Plyqoe{(OI^i<-fFt$8$V7*_2$Z%AQug-=C5 z73Fzf=!%=M=AwTe);^W=nx#v4WG&9Gi~6`b#$~h^$s&tik2g_S7=ViUX=PQ|FU0L; zAmnZwW+#i}y7LIS<@RI7`r6EB7J<7&^=0}ln_!%du`e$z>BxG)@rLz*`^xTRZHScfrz(d3xQ1q{Dr0`;8dmj~ON*68k+lGPPTRFw$K* zmg9cd2hU`DtXHZ7mD&KPGuKxr_YsBUz11nG$FQB)|w!J!v! zH|x0KJ_osok~#^C620&CpV3lUjP4b+bYQB(L1dtC1|9SpGDY9}B3sCP(eQh#WlJf* z$hvKxdF6plQ#kr43^^G!`_<5Dcz3{*Q9VrEafO|fXMXP>T?W~uNXFw)hLNhm7(j3% zxc*Gv(nXz7p~*ej8q$#^ZXTRW_HiR)D2oS?wMVS*Ekn8rZ9s=X*FO8J8*C!N6W>jQ zK0&;`R~DvML=D=`G}wgMeekn#@TthmFnh$C#_)8}+ymmhuRa#NY25R1cC5DZ<1F4XuWLX2Pd zrVPMmqEjR`H)u77M_im1u3omN98{GMgn_JW@`fK#?)Fs z)Ky(2m*}Vz6M+b4AeMlGYc;>Qrd;UqydgYx+6-DoonwDV>r5T+_}o9Lo$SG+A{}Op zKT&VR{+E!{F#JT%g`@%XFJ3Zi`zyVv`rLIO^laph45ypGp)S!yopH z^S4zBjH@0r3g4#V7xtVtr%n$1?Is<}Q;!~}N!5c{VT?cqAOSO&BW|l+jOr|v7vSXH zX73*ckVBZH&x0p?4<}mdh!t@HGceW!GSt7y67Im zACpz%_Qd8wzq3P5AxjWvyy8eIeeY?}tlT%9X067H?L}WlYGS=j@HKm&*Tv@N?rf#~ z!p8$(JMMHPH~CS}!CQ`D^(uL7DqXi?WqQoY2+Yc%@Amm6u|GxghQfG@X5-bXKrMUh zs+F|gOt(F2+Z(=%Fuv5(x7=$>V$&dN!yrEpeFG*O=#}3gvDppM&GDK{nT|`UY7G3! z92d(2<*RE`r34o5u|hkLhrA3G$-94S!0t!{GtP`1?;Yr_d*PG7)Lg05__sh?Ab04oOS$r7B;#F#Q-th&e5K}_^;}iWLWDO> z!y;5cnx=B}zZ>Zg2`T{1@3%PP?oPSr53VR^63R7Jp{TDqE z8(`+kB4yTm*KZ{qTHVVoRmRTwKW|53kKDJ}VvQsb874>A2NLeyIPnbUZRi_a>bc!? zTv!F?$ZG2a?<0KMMp8q^#T1mLjOI7w4&7eC?;=iJXtgB3qgpA~V}SR<=GoHQ{a_$}jl zX^AGY9IAj6xB|ngat#Dxza8SMr6rL zZyQQNS?Z<$GBcR?mQSr|Lf(u4@ItdK`g6gdsKPTHwst64BWMalY^Av^I7X$TAb70U zs4R%PX13|H_hNg`<-5G>?dy55JmA)O8;D@*95|BnYkh%Kim8n-3e?yz3oNL^WdOeKPxJU-s#A;qCW7eSXL}@aNn1&0|`ieEZgKYpC_A*Y;~2 zwzcVKqY3G@0et7-*E)P_#Vk-otj>=&UUm~TKefD2(%4}v#G$#E9h>uQdtb{qqf9Rf z3PIP~&YP78h7IFGI@4na!ys(kOXq+pFu09qh3i9RHA-Tnw#y*3FKO!`XTfwa!<~E@ zzJ$|a9`tOMDD?2ceAM=iULU~xikwk2bWG-2dTn0?x2KsdNjmDfu%Nx=b@ZZdAhn|} zAV|ypxz&4kk!Yq&8=1E7#*SLRP-}4+t`WB$^OQ$-@fVjVzv`k!nfQ41P>yH7Ld>C@ zOG@{<_WQVMkJQyV@Xh7nQ~IT@?lPyvbQ|-%1zs%~_H{qY+>(y84!$8`waXKe%#yCO zz$vq+nJNr76b=1tTWInIpSj`522dZpAZ=Hbw4F&EyTs9AV*-dhN&48VE^II@jC0%9 zOxa47#dsTEYgdMh%RXTqGkn%VaygrD=vRPBN&q_lNXH%s8`@5j<-uQtIX|I?mXG^{ z!_PZTPBhJnV7}0apjqwGTQnhM0oGR6O=hDm+cK(N^1=8ucYWM^-7rRo!O;Oa^)9vo z3T*+FjreQ)3%2o`=Jz09R56G9Lff~mZ$ZI|mhH^GKHF*m^3tCL%}J>e`>mK4nXCw6 zB#%=4n@d0NvJ}Q-TjoPEvQxaDdWhk&{?pbK)&dtpbc0pKYuxU$yuAngWT^VQS;^dY zoJ-5njU3I$v`R@!F;14Qm?{y#uvvWCB(r+5Ap6g)K7)C4w$fyG8i(QWvegSJuilb` z`Rl(>5~z@tS=r|T#~ILOmU2cC0#c&M9c|;n-xH|VJr_qvyWH5``o!Q2DBs8)7SQ{j7$?dUD$>3Bc-o z8uvu^?63D+-j42?$PPkdQpe<$EtF07^>-=~S#IOg_q)3WlBpE^4;!N)Cs*9rKQ?y1 z!W4JLoR_m4xi^l^I5HS5qKYqZRWTPlRiO=p;yry^TUxDqE@BQMhZdByg%hi|q4zlU zT%Va}2{GSSu)K)whE-4s4PcWZG4jUYwVo%DfJ5?*}eId^cvNNlfwz`TLLd#t7Yov@9Eu*76%G; z5qrm>XWgrhRu21^a4AD2I}bIP-JX&2hK1D+B|pf!Pc!_oU{QtkA(ke^m<)xSCc>nA zPv_BRBYgJD-EMvi5q;POS#WyENpGJTub`M@r$oXxc7Jhr1$=lW!36g8_*d83riKlp z6rB!_{)j@m@d$oh)p0U#?c&$e|Iz+{huCw%(1daZ$(IPUKROP{vw#??aWjY)W%P(> z=pnj)k#f48C#X}VShH{`@v>B3GgCv!fYb= zF_ey}hY7!ni2pT}Rx7%K-||LK+)<~%kSMB@Y$K`-RU^aXpsSy(ZUfsy3+kf`Ri_t6 zZoUi)1-mTtLu}$#nKD5D_vXszjL|_`399?Z(9P=t=bQawj#wOwo!OAlt7z4bo`syI z$Py#2p8n(5VP0=OT|KctihRe_CxV}!=G?Ytm)km^jdvbgz&kgL61OAcYs;UZnikPW zfksC`QJ_RFU_{7vhNQLNylsx;g`w*?JM(SG7y-I+Y3?ODD4TNoSndWt+-I{~Qi zK87>C3fLj=XUPsEyw02~7<9%Ykn9)Fhh7&4Hqw6C>c$_OhEIfFs}kgQmWXMcoC%;u z271b|=J>%fqwM$+b%EGjxFNR9O?ez}``}Pm<#rW|VTrxO)E(I2Fagy7cu(rmOh!dp%-DR*6X7*8hJM4bSqMPc9;9sEB+38XWO6Y@;X629l`_eGfXIi z#VxxJa`y#C@UNqjpCQ&ch<-gkQ>5%V;2lp6t!HmVQUH7 zdBE!*sK5MZAbbS-^R@REL^{OBs*h=so$qK= zsWYX=;Z^((q6#)`NZo>Gdm`vDA#|%S-kaqn^Y~|=U1;2iENA_@u zj(evT-bsk=<(Enn+NaQY1P#agLtSgq#)oUh!m@5E zlf0k{Crn)ygm0-eIVOdPNI1vwr^Zh9;WjOYB>qss0}h_4YGykAe5fSNN*DNAQC&a+ z5tIu9*^+MGpz-6q(*@k}@}MH(MK$N?Z_Ok^tsa-_Gm2BK?x;v7&f2_)fjPQO_KQYa z{OymDP)_z}%co1+O0ou`5p<`h!d^4$!kzDT8yB9T1)DRQe}~Ec7NPuG#QY_!`?vV! z@386LoH+8lEk#ey88F2nbF5qS4zX!Ym3hc?pv9vT%Z<$A8or*;rY?nG6SMpryHmGbuM4 z53{JN`EO}RIkF)Ea1pD8_z!{E?z?K|Dxo-LxSzUAn_lA0G|FoMB#UC<*%;t zd+5JN_-|o&39kRYK!AhwuLy8){uKd^|0@C<|AYV+=l=|Gd-?7m@uy_}Eg+1nZ2$eDVBz_3{3{~0v@;eN%+3*MCe_xlwm7t0^3g_WK6zuB*TC;$Elnm-eSe_t@a z(}aK6i zTXD5Nvve}xETNNw{Y&2NchdN8X*(V+F1Ej>?Ve|!R{?16BxNK45D*Z6Meq;cc>y2} zfP;a7g@J~Hg@uKOheJTZLPkPFM8e0wM8zU{O+rlcnvjr`l8Ks>oPmOnkcOLpz{0^JAc6^M(EzU?AR%8tK|(`AL4m1#!1n=A=+GEs ztfDZO%0{r{j#zB|Fdr|p{b>B{-P}Dq13v}@hkW`R8XFg%keKx4YjSo@ZeD&t zVNr2abxmzueM4hYS9ecuU;p=k!SRX7sp*;7x%svAjm@oJ+dI2^r)TFEmsi&}w|6gk zK>#5CQ0woS{TIE^!Fs)df`WvCebEczl^eJ~qC-KGvBF@8D#IE%Vv@7@!(oZVWL0*; zQ?RR?U>iG)A>dGQtWll5sP$4iA;924q635h*LO789}xZ@ z7L3F%N0mAJ73#{dTB{IxX4R*P-LGXi92Z+JhB|fC7ln^=f*w9+7IvwO(;OC-~1F~K(R@|)F^e|=R$l@JcorOHYkqr9_P^phz7Sr>;oh}s+&FwDIO5!>jE__er8u1t(xoS{YlMF&w#Gu$!EY0 zy5%##?{(CT{$0l7D@Nht4JGT#cdb!&*)38}xV{C?0Ff}o`zfym=%QNV&S2mz{ z1|YVBt&u5F@c3XQyymv&cL7)P3}}xK@lh!^S+FR_cm@Qm6g>k*$tRwkIIZ1H)_b3t z*JT-*Y`KsdeYD z#Q(v|di{nNH(Ym*rEHgMSRD13xxU^CRuh@e>e!?zJCugEE0iYl)t?2b%&*sgX~LV@ z=qjv#g*|F`QL)&2T|qO4-kJdK?1xA2*Kt-u>Fij@g$) zpc@0be?S0udQ`6y!O1#dnDism%h81;>5O9-C;CnqhTbo+G6U4DKA0y>lqqYq;VLv1 zi$6+G9ZaoGS6;g_-FGFYe&{)~92Y)f0(|cWn#_glPk-$WNmbYGJq9IZhqmTf;@&OY zXv1^c=h?6@$DO4 z-ZDRZwBCt=clQ)Va(7*jlSCyCIG2W90fC)H^Z?rXctiM>$ff-5A?X<)#xd6!Y^9z% z79xSAA_xE*_ID}ESeto>x)XkM6~2QnGj56s@jFxE@Vh(kyTBApGhAyI&$*d$<4C8i zt}*uhQVN-UHN2TAyRot;oE7!VFU$w3->|ZB( zhZBtF&sEvb8S|;{!4u$qxi6aM(Z{hgVwoLEn~tX}fd%-ZXtp**_VukFnS5G}$ORqb zP~R%m!W@{~WOVVFhmi&_z8w|_kTb*Q+Ta->3>XUMV6Z39<5~Ls3Ns&o23+*msatyKu!mY=*ZX+Jj#SIJm zBDd3iAD@^)1&4rGxqe7)h#8ZiTP{TOO5;od5n{t$+WZWKn!<`YcjLuYe89+3v+3lZ z5;rtqwHVM%l=u-1Io>YyF-7nRQ#nfDN$84dgA6f&ALmySNXN%rLeu*x-Zpl3r}a0X zwscR@vqmu+;Q>M^-*kKVsLDgyu&t79W8w}@vrJLuD4hK(Gy2**GMhNEvW=duM#hbTqD0F2l>!`R z>51Z#g?=0MeBVo@u6TalYH0?0+dczgvb&#J`k#U)2}XP0<=*ND42II=x&>M8JdsSf+)_o!R$C_q ziSuYcS_rFZCxCZ8$Y;PN^|xoh4Bj)~fh&`K^+6-enZLXP@*%?3oun)I zDPzNjY5iEbB^YmuYSaCbI)S39p~%>=+<9I^YMeYFO7*Fs)EH-2DVrDlcH+_%J9+-N zPgPgrPt|o{|I5Uc!W+BwwbO`WB(KV+tAiQDQ^+T&C6lLS@N(C#BPN=zWkF6%@Zd|2 zjILX`-_OJ4GYZMg&b<)C=PbVmtUEj|+DgWd$gly0^-mRKW^DB+~EX*zn zsl#WYmth{c`kX+(e4?=U#8T7K=dyJXDYgcqB$T}-7R4eiJbMDJNKz(e_l~YLZBnz& zVXj*rEqA?*V+{%!`;NA&zwD8 z%DKa%PN+P0ngLcB$Svx5zzjLBPyMI!8SKIzZrk6XN*OMt8N8rQCZ|iY{Yne zD}#HWjgOMtP5=7hd?|l8-%Qh0Il>L~Gk`+=`q6{r5!rf)Usb@{{8X!U&5%HN)3Wjz zP)-JZPO0@Ow>lY#KyO)QoY;uKUn=RtmXx`j_csy_jbhDi(&t}$286oE;W>M3s6K%F z(VhYQm3Nwk`41SARNYpdd94jc6=cy*<^bQCClgYpyMC$Z%AN5`n!uOG_dssKSXmc30(=mHz`W!0mif+U@Ven3^ODk`tZucPtF#fUTr-?bNSyDGFG zC~thTMBSiHC@9;1JoH>qnkG9G@KW9dpUv4F6x8`OGuVXeNYZ(}gZP#XEx>H+NQn_H zLlAGYBZW=m>W9XmQ9FsN8srCZ?D<0C0)BefxP84rKV87dvv@`)+NA;hr(Y77domJc zKH5IEB`z387(enOWXi9r6=kS5nQ9M4_9n+}pLJGh`;oqnri9LDgB#@O{#jJ(8lUr! z9DDIDE%O;rH>kL!_w?-)QNHGLPypw_&8-y8*K11iGnkg9&`)c|^}r!)?#f-Keb3h8 zZp1WQdYD}GIq%4@&v1An6=j%;H>K-gNK9Gg*n21?V|cSq^!>Z~wMh@L9>R-p&j88S z=gH1Sus@Rb=q8f4EHC)RPBptXR#qmEHQTpGzsoFkw^Fo)MY46b8BsXV4DqJUvr57>Aer)=VehWoxPK>8 z>xAL}BSq>`_N%3NEujT%-^~__HOD6dUmY@{t5veWs#8p_@UTEb-Q*`@S0gj_P4g4_ z!faO9yLfwF~Oq{v))BW~t(;RE+9|(YtncA4^)zW7JY@nmZ`x7Z*E? zGX0y2y?mDX0AX^>8S@71LPdi~X7&vP*HKaREx3buMZH{wo4%mn`9*lCYc|)^x7B%h z6>4auzW;_WouDayi#L^B%*@_tQMRFT9RF)w>Nc6p|^7BYIl*|#{qs6A-h7$9v* z(zPdAE{07Z7$xMq1#2cQfu#{_B!Q&_o1^VFKppouAbep|&RZ^3qhT0?0Ru(CqyYB} z$PaE=ugGa>RR~>>8U>5FxXrH^ymE#Q{`XMs_xMG#K=aE37ufzkSAN^StCg>1QR)UO*zXYD$c3-Lj+&Qqsvo3 z{!(74 z%Z5yB`&H@HmP9U*MHf+@a7JvTNp35gOc2P(VJymHSERap;s@yNho375A&mCmSRyBj zQT-p-9ECnTx`$IeR za(@}@bg5c~qX6ArOc8DX2i&lHjFWIHJ;}a~Ym-olDah8u9usw4tItoUc3{tdblTqD zo0=<@G!$7zM-^)vz2R1(t`cz+WuvHwQ;}eC&Tw(}WMQ6jDV*k-Y}2Qlg6Wt^fc9{4 zqi{VgHT}RE)i!2M9!e9fhF!8svKuL!4RunTic{ zEBG`mh4R8|`MYteeqxu_B?&}FQ)wmOWgWl~@e$WrKcO!XJo#(*U2pkyHAYY?wT|0z z_>d#BE69so5slp{ba8w}1EwAh*g5*|PpA28=!5}Wjc!Xv2iQqkCva3Wc9qq_2Bzj>Il_6sPI?iH^l=j7;HX;#QM z7Haoe#G*}~mIS4ek@WXLdLhj)=ZVw_v7F}pjEE>|2s!8@U1^471-8A1$KRibld0=} z?N!$Aq0Us(jJS&<+ss1^#6zYu`a}}pq~Xgkm0LZ=Nngq|%_yvz&Tmw;maK069XX<5 zA!dTNX#3P><$k9zd)!4zh%=uVGr-jCw)3r=F^`ATTC(Hau&I%L5;@={&%*=fG3&lWE-<0l?o*CJ_hbx#yo*v3?l%D}`M-izJ zf3!3but2hn8I)d#5?E^SSjrNBzZELghAHQL8Sif?`xd5st}=8*{iDgvdiD_)x=<&_ zpIpRWwJuJ3$u4I!Cz)eu7O317z|S#X@LtcJVems-l%17U zi?tU~cz}ZodvPFVNo2c*rtQh_E@7>Xh)#r*vt+ zz2tKk%2u#CBJF#4iVt2>2`5>y5oSi;^pB=AF}-8f+!0vMu9TztXOjC)KTm6lRder~ zP<2e%h9kmUs-7dkQ1fgzl+o&;3Uh;wTEO^ecewnQE`D^c5_H3UsxZwc+-Yrv-#wPr3aCVMYvb~U}X&JnpMA)PWZ&w>~5 z5vJb(a8<(3jBg#gGp|waX!5=eHwoTumoU1O3>Lwv-6W0Z_EC262)c?Qt5{k*+zJgz6=A4S1v;@mu`8X!*HP-)UV6kzLg#pbr9fYx?B zq`*6z&uThRZ(WY_15!QCq!fOyGSD{?7;qf(Jp$n20bGmuM#5YOKxG5(6J>BBzqQ)c z<9?bwe~t!ppU*}LPcJ#b}R*kfh)Xx77W(O-b04v;$IfG+kk^q83MrNkM) zsz-I$V*$D->7FRT?&?i?a@MBT7saj1NE{Vr${^yoIW(&kAC4gQpO=y#?z!F7g*HVz zIJ~W@wD8XCX59|vy(D~uD;M-zp}aoV+3rqiDxZG_)Vi9vcf9v>QCn<4p7R2Uh%p@! z#hkqJGqFrI-n$M%N#h%VgbL$NIx^qfMDcCh<`}qO^Ch-a8;>sw<=ZN~@Ulkv<$LDZ?5MIwgA`k-O^ExWwE|)Sf6GxwAP$1u^k!?A)vDJFh~Z zYn;tsprx^_&lC~(nGoTZZl3{}Jf_mra<@F!o$W*?A461=QW@6a2I5Q`Xop_s8K6Hs z(`%WdwV=hOXkka5Xzrl&3Stqk9XIOh?BD0*rvrX3>VgWhHn%3OFJx@}xbDSsijNkp zz?ZLEM8akDAcV{1cFjI(HB%R@FMsvcQYJ^>66uuXYLg8JC zr4jb`u%A#{=QDONK3=Lv-V$Z=hAWngwm_~N?4*U#d6qRQkp*qIVR7>UN|1<rbDi!ChU}|co93&-?+m$Du-NZVA$2lwidFq7l8YYHR z_J&q7g>0vPfW*0@e@4=Mi{s_K)-p-!DN0tBHO7))(4?5xm%(?2l@#_>COp_eQS0!L z32RI2GF5c}zph4q1~VAfM~EDzR#JX|@y++I<*?u3N=~cZQ?cSkC0BGk)0I3T>?JXZ zStx?6=(U+aEOZ}HuNdb{vgYS%8>fz7g86l}Db+V^uzEh4&Z?8>xm=F>d3D@H*P2te z*)1KX&eb?YdazDp#2^zj59%Gp;x70h6RGR3QQwj!H>oIt@y&@}jST_#?$I4;IC@S6 z4gx;B>otX$mUD|;4JWm=%L(Q{!L1D$vvpaoioG`xAqrxvCyw8@TdDekvf3sh@~*P{ zoY#Tr^dTwn`8r3YV=CklW_m9&6}@I=#@#^~JJV!`0ssKM@UGd%ocjTG5;A z@1}EU82ObX zhyx`@x|bNHMjv5DN?%b~S692|)O+j*iqJnT4PxNIYVrv)K(@K@@Dg!f+%wB=8b;&| zsfpuYT5jZiXtnjXhyKHoLMq2i@~(vh{JJdAcjzd&jXU@SoeEw18jc?nbCe&yq>!2L zfWplKEUIy9eSRF@ytSE7nJ7CvDl45)j2f)34=+eW+i>7jQ8)k%`>-lYt){@O>N3|F za3#jx4i!|F$D$l|_$)1ehL>hbD+}jSglliZ$%Y~EW^uM5A@O++BU_7ubIQ7sO@@H9 z)*(?WQB+^-sI87RwuX>Ekx%h7btvET73h_!lQ(*s^0S(MwReOqSFSCauKj6y$~&Ur)*|euOrkun-z(Nfj7z^orNv3 z3eiWO7S`!HeKlfYU$m!e8L7)fOLzO8xR}81q$M3vBXGa&zKkM*3KeS~U`g%}zFBzR zsp#7O4A4!Em3LG4C_Szq)`!UMVH*xv6YcmNDm-R9jGa?ufsF{Y`{c%lGGKUbw`AZA zzqYnE*}yQ?hO1ESr2Sg1A4Zyk756tM<1;8Lld~R=;YFKa_7T15gtjbO7s7D~vN=TC z=%)C22ZEaq2cjdb=KZC8ck#K>UBf&|Yncu|4ivby6W0_)CBE<_FW<)tuff*xF)prpEt1HyMqr`?NgK zQc3>fAc*@&OYZ~M@it^&eX2{L)F)f-!e1_lcx}UWR2bdlRW$v!4Ll86AY?V6x-_IB!bLVQ=k zf`$}0;VtT`HXf@kDeKgG1Etvb86OXC$Z=TG{Z}S2d8H*+LHh0{Vic?GVxelPIb<8^ zmckDr-Aj7QC-m`)1)_I@5VRpm%)f$t)pdo*W5Q_`XYZ0rEmsQ}jyV<*rfR8uc)c4S zYrNjN1F|6a&|n39z^~TE3+R+p#^%Zml97XWWrvOZdZ56-iAc*UzShKCskt|7K&TJ5 zHkn07@Pv0>D%B+5#n9{rFJ)0*IltEw&UI~18G%p(R8HLr-GYA@$QNI2mbI1_;$c3{ z$xuM5n-Pus@s8moN9GIhD<$x?(q06X3w3 z#ODLDxUnVCsh%>OEKgq_XQqWV(N;RwOThMWiU{APm%Mr0DA9Lr1ag+NGs>(B@!( zRvrzui17!ARFn)Fp51h7t5B?DSgap7C*!RA<>vHt5{N-v#}1np`!P3@NqmU<(gyNk zKEo8KK}rNBiGb6ISXN!|H=T?IFlj7iHx5;>a1AXH)IEq@XE!FrF0%SHf;nW|gxBjUY6_%ig1QR!2f|fQGAeK!@jyu#8fojHsQyGe89FazX1kFgE4&}g^QqoZ@ zU3t5DC%COzT{8&H>;M=+zI1xC#0++nx28nM+PO<0rfIVn0KmpoKd(B{0lwofBcD;% za=_a8Tyonoh)nB*rTA-~FuSHj-j7+DT=2^T`AaVF@3HUy2X)lvx&QXuBip|myHPZ@ zQgyavRv_hI0iR8JIdDkI&dtOAx6>rx!#4l=t#4kgzaAfPPfgQb2qo>0@!`H9*+XqQ zONZ~P(Bf)_L(IaTg{?+qEMi0^mJd6HJ7`}46|?JXxaiG#Rx~vg&?y2pKVvGZqwl6h zoqs>H*!_MkP3;>_QR}_wy9pwK_S<=+p^lu6ISuh&Ia8M9eaFAo6XnVAP#&IB>KyNT zksMVO+2(L1Ajuz<9O)66liqKGIYUgpHt)@Vq)+f3#B0?%q8x?r05v;*3VLNpL~GIz zx-hiAnU)_A$6`bG!D36g_7d_sb)GjW_*ADqoW($q2X(_GJWO@GqvUlTL4)n0bn+Lm zes|DTj!8>U$__MU2oax+gRuirf+`;?{X-84>uIV{m{647n_&W}d`_4HEk{2hGpjEP z9hb=0+!msX9Cl==iz^+Mknbx@(i__SPpO$+!dhIh(Te$a{>JweVNxQ@{rdE~?o7-LmlD!^JwD&HWMqcX zb&N6(hH!2pJ(2hz30*w zAE#g_@-Yz?-><*ryZ8~uiZVm(Z|j1Uj+ESPOuLAy6!UPoelF^rK!b<(qL{nxe(>1^=jvaA)sJL$U-+OD#nV z&-D*TLEQcvczA9L3#8kYPK)ne=^e+FP~Wn^Xpg~mVA@Pk6G0D8G`gQ45k}A7?DH6X zf@*P$9MbNPAI^`>O_isxwzMDToLFYLq)C$UU&Tg*oaquRko=Gw@yqhUv4W#>0_JN1 zlWU+RJUf11B(hf|yiNy&qe#-n?C>D$cUsLJOzGlwGef$fS8>l!iRLOk8h)d^4lx zkO*a{OyzuaAsa7foNl*wsbl{KzaaaO zP(~87N`=Cmrgl8X9dYVe1A{l$1EOi9lvctIY$31gE6g0#A58T`q&yoCk?%88nYf|3 z$sz>0-ZFWmhGkLcGkK}->;`XBr6mb>bDwYJ==rDa#9oZb5mVhkC14w%Sef&Xr@m(2 zEOD5mX>ss>l3+6L&1^^#Gi0v8?(0Dhgc4-doG(`5q6OVpqo$bV}Eq? zo4gowA8>{k@4;72q;KVk%SzY>@yh;89m}M{9xhZ(WO7wWqPygo7_i<5-$DL_kCf4~ zlZt+cMfb7!bx$UZd6^ec>KJL?X80Rc9P_tHZ+AMd&&S)GgA9pT9SrxRI7iN7g&%Nb zMJ0OP0sFEg1zXgbmC?!_#_4>bb;jsCIB5gmcq?9ySYO2Vo=R?b^rW3c$T_~^bRhPa z$Gc?rX<{VRZwixG#g1&lBQI~4%+)JZ+ zKy$V@NnSi^^HSVhvZW~8UFXCG7KnUG6{cjD{&o2JVReqvpN4Qh*;jMGFj zVcp6(4yNOBE|@BDh+5~_`Rcf0p_o7L%J zIEHum`;XP41jr|qoe68>r>%D%sV5}vI0DcxB_$&<1?bfl&rONtq6EI0V&`{IciU!_ zXG|`3eRmu;L=fD_b-`;u|E%yPHCzGPx2HMCj_OT@W>-Y@P(wDRMh%Ol5WlLy9Un33 z@IC~T6!R_}1&*0~9|fQP%8&1kJyJNue6PsLGPx-nP8~mveTLWis;M(V>CVQdY{}fN zaFOaHK~CqH6)7LzD>aXVp9o{TzuRAzpqu-!2H@&JcP!TPhH4^Sj$!+b4hv- zQG-ZkzYN~pvl*|sWScxvn7PlN+m+I^F5e_h$%YD-n!T>{(%M2vY<)k`UF=qo7^JT6 zR5%G&Q7jXYELh?T`|XPs6L%jGLma`>R$ogX&?%>BQOm7D&W^*7XPDsqPf97!K#Uq= zM2DO!M-tI)q%%hx@y|S|rX29MfTTdTF-M=I*x|>00v45{>@afgp&BVjmMEpK;hm&} zn`EMCObyJB;c5cArwzW4v@?p)wv+WI~&PaGoAags?>6jelQVC&HQd7FuyY@`E%>KXU zdEWQ)c|Py^dHGDU=D*ioYyH+1>}HV^C}yp)|?9w!xYHF}|R2;ITDJ)mLe+Y*W8_+@P2-_|3A8v)fmto%D46 zJ`e4cSJfwN9lr8s_3zEB?~dvB{RHcUL*3tP2rUaf7!tYlTkTri3x5Vyzg?JlR^-h2 zwR?Th<3knRPc0sqW~{sJdA7{Z>PW!DMV!s84z(Rq*WSD;bDA&WhatIXNL$l0$L}>O z|1mhaBGytEyVUT|#VzYcEOT6qZPX#xTuY(TQlpHO3plNg^Q-t_7gG)_O?7lyK2sQP za_7VxCyszIbx15z3wCV~IHlXi2H4|h60tSn6XoJq?**K8$Ezny9-W-?A;r($sirw& zlG2bgXXv=CK?iOaMwy9C^(H#@s2LBj-w_r)sevc2SZc*_&oSeF))oXok zk$Kn|odh{Su6w+$y!Omts73QC#g>Ns*wy?3_bVQ%V>e1Cy!YLSl9fSH-A7%a!>NA;Y3BT1TVN6Bx`Ow@hE7unQNP6uuR~3tx!VM4Z9Q{7CEEIKIo?m zPQA^hWsQqA&2OBu$)>}(V_t`~ze&gb&EEt@kJvd*-jm20Jf0Ry@S-+GPVuY421ZWY zJxP_kDlvO<40pbIZM)m0y50A)^_ScZ@fy>9baRb7f0|E?{M9s{cOU=M%4@iN?u43J zZQ?BDpWl;krj`3@_qj0Kv@=ua@8hp&_xJby;rzm`O)-~KOx+?pU$04yf9l{eaHa*i zT9jNUQ5YLIhs|71GHZ2mUN^QLGj$7-93n+nn)WXLdF$)sqD41{7QM2^ZM1jwOK&|8 zl>JmHIdq5{YUcXTGWd?bHN8->*}*mao}{M1qpVosE$3VhvZDvajJerzG$8c*GGF`W zk#92Jq`f)zCiYE4%axXdmYwe%Ta2nrK8*PG&4h+%fz!a>(=J!Vh1bx{A$iSW+=CnE zWJkxJnUZzGBKKkpKj(d5iuULikE&nmgmRsBtYF$7qShj|G|9i%`dsJl*f!JR`Vfou z@MkhdzOetLfiD*AOkPm;V1U}X{1#57%+U!OC%q^MPnIXYmA8NW`8rXAhRH?;8;OyK}5zC{{-RA4UVUb zOigyhwN{_3U%SJ>McXyHQnR=;?fu608r={0g!-l8U*DZvXIJ-Ygj#Lp+4gPm<-WuI z_IR!C@cq>;I>eC#*;nB&173>;c$FOR%sFTwE5pXQdO1fdWMgy{sZ$m*7iH?ag)Bjt z%CL}S4KY08;+ojmSDw`TZSHo#>W*a^MU47-z-*Ay37(^%W2 zxl`>_n%w}eWS>HZipGWeb^n-G5qDT#T|16Aa&_0!mAU;MD?(udP7i!YZC>-*R_VR+7R zOPPy4apY&YPL8{!EEp&8rdVkyOHig(S<14MDKAS|nLgeCmzQ;TXlgm4#v3x5xtO?Mq_Ho$)A0s$W|Y(`9MZ|5AmSdc0>%(Z#>s zEM+z!Z6})#|5P$AxX@-x+X!!s+K|1Q;n&X*d-x zQrK2F1RPeR(s1fH9;iC$Iyw7rpNvrS&H+o=*%8FE+Okt0hKRpC74a}yJ(`SK9{FQL zV01|&8Zaw-YWgvEub*?)>sbXCU!QSzZA!L>-nYSn%H`GT3=NNTCg!xn7mm;3j_-e| zm=4|fa}vEPcl!suwyGMty1|iZwTG-qy=;9n6+@<9tzDgFWS^_%=hYT>Mtw-4cjs=O zusYRMn%MYmpY9*y=Qgc<98*4)8GI=r^8=;V&D^KQP=BVaaR_*VTQ0f0DCZ2`uF75P zoTcmsW$HXuqeDC^n^83;Yy4kJSXUfxv!=ncD77=hiZ{3EZgZAtndz|~ZWN1u?b}^^ zT|cC?C25I>TNc{sSO1~@N%PzA@9&l`3n`F-t4;h93fG>_4YdmGcv2A>6wsFRyK!elVVkEUxV@u; zZc3HAo@=Ywb4CAfV)nL>uEzKGC5EZ zR#qX+^{p2xy5AdhwLhwJ%?u3c?zrU{>M!pqYj!xC(7t15aY#!`OYy2ViPej8IvSgE zMnC0uFRJdkn~~t=Gd1Fjw>Yz-bjbyd$JbR=KXtBJOLzK+Yva0IHl$b260eq?yVnkV!9LB>mG!9c3q$4cX7+s+3)(_>9kxQc;mp7-5;{mokuqB+K|2=-uvi; z!1y~SSEP?R=v~Zd_q>z8F#W|(-u*a%)SXv$=}!B+D~+713*zYXZ+`F|X5@UOAkHNH z&q#0Uan3sn;>M)M?(+6Ebap5R`6^xO?=@Nm!R=B9Zty#4s9A8OG;K=o18K$N;M-EI z$-&p8R;Fdgq(J%Oa$liDiLOC*!j9((nmoZqmz~GDm6E__A5jhH+;nO2>^m zJ4z}xJUc|X!yrLTe4e|xQCx23^jbXFjQ?0Xb;{4ex-;i$CIa9^vM*XIUJ;zn)i(?J*w8gg!PV{TYbDfr3 z*zDV?JGsKEsyHR+{!dyzG;}&lE4))ewnlI&;$Ft=4T{{aHNK&2#x&ysQ}1mTCv$_U zEYD<)jlB}rK+Z3(zjK=2W;B7D`m%F>=EMD0rZi~VB(_vpFHPKAb-^idLY1LIV%E$3 zvlAU(s?BH!u)D$;6!+56&(>~V(jPwEhu74&*yD(0tHWb_47$gj3vG|@?&tVK5DuOg zRX$0rctp<`KY+(PMa?td^O@%sRsSQzJao+7>dlS6mHnnz)}vBcF1CExdmqE8 ze(Jr?6*6h%cKE#KlsS4zRMf__PT4Xh-{;E};{x}Eq7ObBCrln?UZrk!KPh{J+4jmY z_222G&$sQD{lwJM+n35#Gvnd&o?_C<8kv-$odT6!W5lZLk)Jg8j<`<>l}bPLk+P@0 z{L8HMsSITcdix9-8@+90)?xA~d-1P(N?HErdsFW==>1mic}%{Kd*7Gig%xRHvDjl3 zcfsla4;=D=<}PwJAaFcqCKRwTR5UJ0%w?iP8z|yIaP4fIEs%(!ZD?ysKni#wTZ))r z!?&eHBGMY!SQ9gC_|yJ*4qX0!9sE~Fsl@)}KirzRXyfSH_440mFBEM${Umm7IGG;1 zr)IxkPC-=76wyN4uzfDJVNokj?-?!<96eJLJEi)=#>A4=7Z&=j_ZBt#cC}?@i{@`z zvv$EY+AOMDch4lwNEflsdb{Gt#f!!K%%n$04!qL5a4ISM>RUI?lqazd9yrcR9qkf( z+E?>j-t48}ruJR>x=thOsB`a54~WYbpW5BFctqiHbMJ_%`+ZE^`<>Yn`^Nyy3mSUI z2QJKeyI9l7E9#Zkt~HmYYc4tV&BGf@7Jps6eBGJdn=hz6ygk-y=flzg>n;ow*alv@ zwW@Tu^YV?}kwZ%LoZXzgch+7QGu8d8nuvL&0~0O`+G49y6IFI$)Q|bgO22Mj?o+cn z`2IKT$43pBuC-|vx6wwwG2dD^G}xCn#M)|fRF`n{?#cm!Z_F2JOWX~9{LNQ5;`WN6 z^Ku6YM;cc;9@7F&U$_R*v0s%RWQv zMSY)lw{5<=vGZWq9sdCf-t)%h8TpUiTWM$U_vHHFC*4dEf7@iS+mrZP(TWMnZbTmp zPvd=cv0h_Y{^^6!CG~pcxug6?`Bb`O?ri31=|2med?P3*Osjt2wEP)CQAPD5vvYMg zDb=^=7QKo}=Z@`l^+qEqJv+i&>j#D0_F25g>b*`^u2svZ%Pl2ZZ(ZL$J{5XDXX3I; z#2Q`xp64RX;idoAf9n5_8<{VvVDwI0X6tBntt?qmQZ;ta zoB$_Dp$8E z5@Sf!Cd7V+ijCr7k3hvn6D*s6C%}`R12NbFm&Y?vJ^8}_+a zHVUxu0UNN*Nk9jVqrfDSyru-652;Q4@^575CJJE`t(0ziiV&_M$_@O;H856?Ml8=!**bkKkf z8qh%lI`HuTyIw$t5YU0=XV$txK!*^}fsfYM>jFB2fDR#`LkQ@=$CRu(aFiXk4bZ{7 zGedQon*%z`0UhRm4$LEVd4LXcKnG@&>be9bG~343LGai*2p(Gp!DH(ncx)X6kFA5? zv2_qQ+7!EefDX)6RzBt&gG%2MI2;$-2IwFF9XN^-dtE>Wz;l8CbYP!`RUW`|9B&WE z2k5}T_}JwEI;5h;CoB()E06fP5s(?BG9RSY>fae6ja{}Nw0q~pvcuoL3Cjg!k0M7}4=LEoW z0^m6T@SFg6P5?Y71i=0Q@SFg6P6&YQ0q~p<0OJbaIRWsT0C-LSJSPC269CT%8raVP zo)ZAi34rGWz;goNIY9&a2f%Xz;5h;CoB()E06Zsz!2SX797i`~?^^)R34rGWz;hh? zj$IzWa{}Nw0q`7$_yfuV#+5ms1K>FU@EnILWz~V-oCes~{2&3IlK{_2fafH@a}wY= z6FpLuJtRKfXW0OrlK{_2fafF*M9D6Xy`Pf+&q?511_|(-1b9vYJSPF3lK{_|v)8OP z13V}3g%EZ=_P8QR_P8Peo|6F2Nr2}hz;hDdISKHb1b9vYJSPF3lL*iO@SFsAP69k9 z0iKfp&q;viB*1eL;5i8#pOXO3Nr2}hz;hDdISygW9tQx=Nr2}hz;hDdISKHb1b9vY zJSPF3lK{{0wE?ycfaf@zGmsD1&jFs30MAK)=On;$65u(0=^(o;0MAK)=lI=~z`B4A zfafH@a}wY=3Gke`QpTD$0G^Wo&zbY5taSmNlK{_2fafH@a}wY=3Gkc*cuoR5$L|Sc zw;A9$3Gkc*cuoR5Cjp+5!0|Z=@SFsA&YVM5?Pmn=905E>!0|Z(c#Z&`BY@`!;5h<# zjsTt`faeI{IRbc&0G=a&=Lk4HM*z0M8M?a|G}l0X#^1{D zM*z0M8M?a|G}l0X#=X(4J&m@-_np~@ zY|L#);S8EKpTQGQLi3q?o`@FO@<}0SYlCy(`_6ib#>cUr6^+kSsq_c-t!P0{(UdJA zw6(#vy@@Dm;b&!F-|h1(); + // Guard against circular references; only read xref at each offset once + var xrefOffsetSeen = new HashSet(); + var bruteForceObjPositions = new Dictionary(); DictionaryToken? trailer = null; @@ -131,6 +134,14 @@ internal static class XrefBruteForcer ClearQueues(); var potentialTableOffset = bytes.CurrentOffset - 4; + + if (xrefOffsetSeen.Contains(potentialTableOffset)) + { + log.Debug($"Skipping circular xref reference at {potentialTableOffset}"); + continue; + } + xrefOffsetSeen.Add(potentialTableOffset); + var table = XrefTableParser.TryReadTableAtOffset( new FileHeaderOffset(0), potentialTableOffset, @@ -152,15 +163,22 @@ internal static class XrefBruteForcer { ClearQueues(); - if (!lastObjPosition.HasValue) + if (lastObjPosition is not long offset) { log.Error("Found an /XRef without having encountered an object first"); continue; } + if (xrefOffsetSeen.Contains(offset)) + { + log.Debug($"Skipping circular /XRef reference at {offset}"); + continue; + } + xrefOffsetSeen.Add(offset); + var stream = XrefStreamParser.TryReadStreamAtOffset( new FileHeaderOffset(0), - lastObjPosition.Value, + offset, bytes, scanner, log);