10
10
import traceback
11
11
from io import BytesIO
12
12
from test import support
13
- from test .support import os_helper
13
+ from test .support import import_helper , os_helper
14
14
from test .support import sortdict
15
15
from unittest import mock
16
16
from xml .parsers import expat
@@ -825,7 +825,7 @@ def start_element(name, _):
825
825
826
826
class AttackProtectionTest (unittest .TestCase ):
827
827
828
- def billion_laughs (self , ncols , nrows , text = '.' ):
828
+ def billion_laughs (self , ncols , nrows , text = '.' , indent = ' ' ):
829
829
"""Create a billion laugh payload.
830
830
831
831
Be careful: the number of total items is pow(n, k), thereby
@@ -834,34 +834,36 @@ def billion_laughs(self, ncols, nrows, text='.'):
834
834
body = textwrap .indent ('\n ' .join (
835
835
f'<!ENTITY row{ i + 1 } "{ f"&row{ i } ;" * ncols } ">'
836
836
for i in range (nrows )
837
- ), ' ' )
837
+ ), indent )
838
838
return f"""\
839
839
<?xml version="1.0"?>
840
840
<!DOCTYPE doc [
841
- <!ENTITY row0 "{ text } ">
842
- <!ELEMENT doc (#PCDATA)>
841
+ { indent } <!ENTITY row0 "{ text } ">
842
+ { indent } <!ELEMENT doc (#PCDATA)>
843
843
{ body }
844
844
]>
845
845
<doc>&row{ nrows } ;</doc>
846
846
"""
847
847
848
848
def test_set_alloc_tracker_maximum_amplification (self ):
849
- payload = self .billion_laughs (10 , 4 )
849
+ # On WASI, the maximum amplification factor of the payload may differ,
850
+ # so we craft a payload that is likely to yield an allocation factor
851
+ # way larger than 1.0 and way smaller than 10^5.
852
+ payload = self .billion_laughs (1 , 2 )
850
853
851
854
p = expat .ParserCreate ()
852
855
# Unconditionally enable maximum amplification factor.
853
856
p .SetAllocTrackerActivationThreshold (0 )
854
- # At runtime, the peak amplification factor is 101.71,
855
- # which is above the default threshold (100.0).
856
- msg = re . escape ( "out of memory: line 3 , column 15" )
857
+ # Use a max amplification factor likely to be below the real one.
858
+ self . assertIsNone ( p . SetAllocTrackerMaximumAmplification ( 1.0 ))
859
+ msg = r "out of memory: line \d+ , column \d+"
857
860
self .assertRaisesRegex (expat .ExpatError , msg , p .Parse , payload )
858
861
859
862
# # Re-create a parser as the current parser is now in an error state.
860
863
p = expat .ParserCreate ()
861
864
# Unconditionally enable maximum amplification factor.
862
865
p .SetAllocTrackerActivationThreshold (0 )
863
- # Use a max amplification factor a bit above the actual one.
864
- self .assertIsNone (p .SetAllocTrackerMaximumAmplification (101.72 ))
866
+ self .assertIsNone (p .SetAllocTrackerMaximumAmplification (10_000 ))
865
867
self .assertIsNotNone (p .Parse (payload ))
866
868
867
869
def test_set_alloc_tracker_maximum_amplification_invalid_args (self ):
@@ -894,20 +896,21 @@ def test_set_alloc_tracker_activation_threshold(self):
894
896
p .SetAllocTrackerActivationThreshold (MIN_ALLOC - 1 )
895
897
# Check that we always reach the activation threshold.
896
898
self .assertIsNone (p .SetAllocTrackerMaximumAmplification (1.0 ))
897
- msg = re . escape ( "out of memory: line 3 , column 10" )
899
+ msg = r "out of memory: line \d+ , column \d+"
898
900
self .assertRaisesRegex (expat .ExpatError , msg , p .Parse , payload )
899
901
900
- def test_set_alloc_tracker_activation_threshold_invalid_args (self ):
902
+ def test_set_alloc_tracker_activation_threshold_overflown_args (self ):
903
+ _testcapi = import_helper .import_module ("_testcapi" )
901
904
parser = expat .ParserCreate ()
902
905
f = parser .SetAllocTrackerActivationThreshold
906
+ self .assertRaises (OverflowError , f , _testcapi .ULLONG_MAX + 1 )
903
907
904
- ULONG_LONG_MAX = 2 * sys .maxsize + 1
905
- self .assertRaises (OverflowError , f , ULONG_LONG_MAX + 1 )
906
-
908
+ def test_set_alloc_tracker_activation_threshold_invalid_args (self ):
909
+ parser = expat .ParserCreate ()
907
910
subparser = parser .ExternalEntityParserCreate (None )
908
- fsub = subparser .SetAllocTrackerActivationThreshold
911
+ f = subparser .SetAllocTrackerActivationThreshold
909
912
msg = re .escape ("parser must be a root parser" )
910
- self .assertRaisesRegex (expat .ExpatError , msg , fsub , 12345 )
913
+ self .assertRaisesRegex (expat .ExpatError , msg , f , 12345 )
911
914
912
915
913
916
if __name__ == "__main__" :
0 commit comments