@@ -847,44 +847,67 @@ def exponential_expansion_payload(self, ncols, nrows, text='.'):
847
847
<doc>&row{ nrows } ;</doc>
848
848
"""
849
849
850
+ # With the default Expat configuration, the billion laughs protection may
851
+ # hit before the allocation limiter if exponential_expansion_payload() is
852
+ # not carefully parametrized. In particular, use the following assert_*()
853
+ # methods to check the error message of the active protection.
854
+
855
+ def assert_root_parser_failure (self , func , / , * args , ** kwargs ):
856
+ """Check that func(*args, **kwargs) is invalid for a sub-parser."""
857
+ msg = "parser must be a root parser"
858
+ self .assertRaisesRegex (expat .ExpatError , msg , func , * args , ** kwargs )
859
+
860
+ def assert_alloc_limit_reached (self , func , / , * args , ** kwargs ):
861
+ """Check that fnuc(*args, **kwargs) hits the allocation limit."""
862
+ msg = r"out of memory: line \d+, column \d+"
863
+ self .assertRaisesRegex (expat .ExpatError , msg , func , * args , ** kwargs )
864
+
850
865
def test_set_alloc_tracker_maximum_amplification (self ):
851
866
# On WASI, the maximum amplification factor of the payload may differ,
852
867
# so we craft a payload that is likely to yield an amplification factor
853
- # way larger than 1.0 and way smaller than 10^5 .
868
+ # way larger than 1.0 and way smaller than 10^4 .
854
869
payload = self .exponential_expansion_payload (1 , 2 )
855
870
856
871
p = expat .ParserCreate ()
857
872
# Unconditionally enable maximum amplification factor.
858
873
p .SetAllocTrackerActivationThreshold (0 )
859
874
# Use a max amplification factor likely to be below the real one.
860
875
self .assertIsNone (p .SetAllocTrackerMaximumAmplification (1.0 ))
861
- msg = r"out of memory: line \d+, column \d+"
862
- self .assertRaisesRegex (expat .ExpatError , msg , p .Parse , payload , True )
876
+ self .assert_alloc_limit_reached (p .Parse , payload , True )
863
877
864
878
# Re-create a parser as the current parser is now in an error state.
865
879
p = expat .ParserCreate ()
866
880
# Unconditionally enable maximum amplification factor.
867
881
p .SetAllocTrackerActivationThreshold (0 )
882
+ # Use a max amplification factor likely to be above the real one.
868
883
self .assertIsNone (p .SetAllocTrackerMaximumAmplification (10_000 ))
869
884
self .assertIsNotNone (p .Parse (payload , True ))
870
885
871
886
def test_set_alloc_tracker_maximum_amplification_infinity (self ):
872
- inf = float ('inf' ) # an 'inf' threshold is allowed
887
+ inf = float ('inf' ) # an 'inf' threshold is allowed by Expat
873
888
parser = expat .ParserCreate ()
874
889
self .assertIsNone (parser .SetAllocTrackerMaximumAmplification (inf ))
875
890
876
- def test_set_alloc_tracker_maximum_amplification_fail_for_subparser (self ):
891
+ def test_set_alloc_tracker_maximum_amplification_arg_invalid_type (self ):
892
+ parser = expat .ParserCreate ()
893
+ f = parser .SetAllocTrackerMaximumAmplification
894
+
895
+ self .assertRaises (TypeError , f , None )
896
+ self .assertRaises (TypeError , f , 'abc' )
897
+
898
+ def test_set_alloc_tracker_maximum_amplification_arg_invalid_range (self ):
877
899
parser = expat .ParserCreate ()
878
900
f = parser .SetAllocTrackerMaximumAmplification
879
901
880
902
msg = re .escape ("'max_factor' must be at least 1.0" )
881
903
self .assertRaisesRegex (expat .ExpatError , msg , f , float ('nan' ))
882
904
self .assertRaisesRegex (expat .ExpatError , msg , f , 0.99 )
883
905
906
+ def test_set_alloc_tracker_maximum_amplification_fail_for_subparser (self ):
907
+ parser = expat .ParserCreate ()
884
908
subparser = parser .ExternalEntityParserCreate (None )
885
909
fsub = subparser .SetAllocTrackerMaximumAmplification
886
- msg = "parser must be a root parser"
887
- self .assertRaisesRegex (expat .ExpatError , msg , fsub , 1.0 )
910
+ self .assert_root_parser_failure (fsub , 123.45 )
888
911
889
912
def test_set_alloc_tracker_activation_threshold (self ):
890
913
# The payload is expected to have a peak allocation of
@@ -901,17 +924,17 @@ def test_set_alloc_tracker_activation_threshold(self):
901
924
p .SetAllocTrackerActivationThreshold (2 )
902
925
# Check that we always reach the activation threshold.
903
926
self .assertIsNone (p .SetAllocTrackerMaximumAmplification (1.0 ))
904
- msg = r"out of memory: line \d+, column \d+"
905
- self .assertRaisesRegex (expat .ExpatError , msg , p .Parse , payload , True )
927
+ self .assert_alloc_limit_reached (p .Parse , payload , True )
906
928
907
- def test_set_alloc_tracker_activation_threshold_arg_invalid (self ):
929
+ def test_set_alloc_tracker_activation_threshold_arg_invalid_type (self ):
908
930
parser = expat .ParserCreate ()
909
931
f = parser .SetAllocTrackerActivationThreshold
932
+
910
933
self .assertRaises (TypeError , f , 1.0 )
911
934
self .assertRaises (TypeError , f , - 1.5 )
912
935
self .assertRaises (ValueError , f , - 5 )
913
936
914
- def test_set_alloc_tracker_activation_threshold_arg_overflow (self ):
937
+ def test_set_alloc_tracker_activation_threshold_arg_invalid_range (self ):
915
938
_testcapi = import_helper .import_module ("_testcapi" )
916
939
parser = expat .ParserCreate ()
917
940
f = parser .SetAllocTrackerActivationThreshold
@@ -921,8 +944,7 @@ def test_set_alloc_tracker_activation_threshold_fail_for_subparser(self):
921
944
parser = expat .ParserCreate ()
922
945
subparser = parser .ExternalEntityParserCreate (None )
923
946
fsub = subparser .SetAllocTrackerActivationThreshold
924
- msg = "parser must be a root parser"
925
- self .assertRaisesRegex (expat .ExpatError , msg , fsub , 12345 )
947
+ self .assert_root_parser_failure (fsub , 12345 )
926
948
927
949
928
950
if __name__ == "__main__" :
0 commit comments