@@ -847,44 +847,67 @@ def exponential_expansion_payload(self, ncols, nrows, text='.'):
847847<doc>&row{ nrows } ;</doc>
848848"""
849849
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+
850865 def test_set_alloc_tracker_maximum_amplification (self ):
851866 # On WASI, the maximum amplification factor of the payload may differ,
852867 # 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 .
854869 payload = self .exponential_expansion_payload (1 , 2 )
855870
856871 p = expat .ParserCreate ()
857872 # Unconditionally enable maximum amplification factor.
858873 p .SetAllocTrackerActivationThreshold (0 )
859874 # Use a max amplification factor likely to be below the real one.
860875 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 )
863877
864878 # Re-create a parser as the current parser is now in an error state.
865879 p = expat .ParserCreate ()
866880 # Unconditionally enable maximum amplification factor.
867881 p .SetAllocTrackerActivationThreshold (0 )
882+ # Use a max amplification factor likely to be above the real one.
868883 self .assertIsNone (p .SetAllocTrackerMaximumAmplification (10_000 ))
869884 self .assertIsNotNone (p .Parse (payload , True ))
870885
871886 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
873888 parser = expat .ParserCreate ()
874889 self .assertIsNone (parser .SetAllocTrackerMaximumAmplification (inf ))
875890
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 ):
877899 parser = expat .ParserCreate ()
878900 f = parser .SetAllocTrackerMaximumAmplification
879901
880902 msg = re .escape ("'max_factor' must be at least 1.0" )
881903 self .assertRaisesRegex (expat .ExpatError , msg , f , float ('nan' ))
882904 self .assertRaisesRegex (expat .ExpatError , msg , f , 0.99 )
883905
906+ def test_set_alloc_tracker_maximum_amplification_fail_for_subparser (self ):
907+ parser = expat .ParserCreate ()
884908 subparser = parser .ExternalEntityParserCreate (None )
885909 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 )
888911
889912 def test_set_alloc_tracker_activation_threshold (self ):
890913 # The payload is expected to have a peak allocation of
@@ -901,17 +924,17 @@ def test_set_alloc_tracker_activation_threshold(self):
901924 p .SetAllocTrackerActivationThreshold (2 )
902925 # Check that we always reach the activation threshold.
903926 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 )
906928
907- def test_set_alloc_tracker_activation_threshold_arg_invalid (self ):
929+ def test_set_alloc_tracker_activation_threshold_arg_invalid_type (self ):
908930 parser = expat .ParserCreate ()
909931 f = parser .SetAllocTrackerActivationThreshold
932+
910933 self .assertRaises (TypeError , f , 1.0 )
911934 self .assertRaises (TypeError , f , - 1.5 )
912935 self .assertRaises (ValueError , f , - 5 )
913936
914- def test_set_alloc_tracker_activation_threshold_arg_overflow (self ):
937+ def test_set_alloc_tracker_activation_threshold_arg_invalid_range (self ):
915938 _testcapi = import_helper .import_module ("_testcapi" )
916939 parser = expat .ParserCreate ()
917940 f = parser .SetAllocTrackerActivationThreshold
@@ -921,8 +944,7 @@ def test_set_alloc_tracker_activation_threshold_fail_for_subparser(self):
921944 parser = expat .ParserCreate ()
922945 subparser = parser .ExternalEntityParserCreate (None )
923946 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 )
926948
927949
928950if __name__ == "__main__" :
0 commit comments