Skip to content

Commit a8090a0

Browse files
authored
Don't overflow on complex constructor (#1393)
* Don't overflow on complex constructor * Disallow null chars
1 parent 6bb5a7c commit a8090a0

3 files changed

Lines changed: 30 additions & 35 deletions

File tree

Src/IronPython/Runtime/LiteralParser.cs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -913,8 +913,6 @@ internal static bool TryParseFloat(string text, out double res, bool replaceUnic
913913
return false;
914914
}
915915
res = ParseFloatNoCatch(text, replaceUnicode: replaceUnicode);
916-
} catch (OverflowException) {
917-
res = text.lstrip().StartsWith("-", StringComparison.Ordinal) ? Double.NegativeInfinity : Double.PositiveInfinity;
918916
} catch (FormatException) {
919917
res = default;
920918
return false;
@@ -923,19 +921,15 @@ internal static bool TryParseFloat(string text, out double res, bool replaceUnic
923921
}
924922

925923
public static double ParseFloat(string text) {
926-
try {
927-
//
928-
// Strings that end with '\0' is the specific case that CLR libraries allow,
929-
// however Python doesn't. Since we use CLR floating point number parser,
930-
// we must check explicitly for the strings that end with '\0'
931-
//
932-
if (text != null && text.Length > 0 && text[text.Length - 1] == '\0') {
933-
throw PythonOps.ValueError("null byte in float literal");
934-
}
935-
return ParseFloatNoCatch(text);
936-
} catch (OverflowException) {
937-
return text.lstrip().StartsWith("-", StringComparison.Ordinal) ? Double.NegativeInfinity : Double.PositiveInfinity;
924+
//
925+
// Strings that end with '\0' is the specific case that CLR libraries allow,
926+
// however Python doesn't. Since we use CLR floating point number parser,
927+
// we must check explicitly for the strings that end with '\0'
928+
//
929+
if (text != null && text.Length > 0 && text[text.Length - 1] == '\0') {
930+
throw PythonOps.ValueError("null byte in float literal");
938931
}
932+
return ParseFloatNoCatch(text);
939933
}
940934

941935
private static double ParseFloatNoCatch(string text, bool replaceUnicode = true) {
@@ -955,7 +949,12 @@ private static double ParseFloatNoCatch(string text, bool replaceUnicode = true)
955949
return double.NegativeInfinity;
956950
default:
957951
// pass NumberStyles to disallow ,'s in float strings.
958-
double res = double.Parse(s, NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture);
952+
double res;
953+
try {
954+
res = double.Parse(s, NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture);
955+
} catch (OverflowException) {
956+
res = text.lstrip().StartsWith("-", StringComparison.Ordinal) ? Double.NegativeInfinity : Double.PositiveInfinity;
957+
}
959958
return (res == 0.0 && text.lstrip().StartsWith("-", StringComparison.Ordinal)) ? DoubleOps.NegativeZero : res;
960959
}
961960
}
@@ -1005,7 +1004,7 @@ public static Complex ParseComplex(string s) {
10051004
int len = text.Length;
10061005
var idx = text.IndexOf('j');
10071006
if (idx == -1) {
1008-
return MathUtils.MakeReal(ParseFloatNoCatch(text));
1007+
return MathUtils.MakeReal(ParseFloat(text));
10091008
} else if (idx == len - 1) {
10101009
// last sign delimits real and imaginary...
10111010
int signPos = text.LastIndexOfAny(signs);
@@ -1020,7 +1019,7 @@ public static Complex ParseComplex(string s) {
10201019

10211020
// no real component
10221021
if (signPos < 0) {
1023-
return MathUtils.MakeImaginary((len == 1) ? 1 : ParseFloatNoCatch(text.Substring(0, len - 1)));
1022+
return MathUtils.MakeImaginary((len == 1) ? 1 : ParseFloat(text.Substring(0, len - 1)));
10241023
}
10251024

10261025
string real = text.Substring(0, signPos);
@@ -1029,12 +1028,10 @@ public static Complex ParseComplex(string s) {
10291028
imag += "1"; // convert +/- to +1/-1
10301029
}
10311030

1032-
return new Complex(String.IsNullOrEmpty(real) ? 0 : ParseFloatNoCatch(real), ParseFloatNoCatch(imag));
1031+
return new Complex(String.IsNullOrEmpty(real) ? 0 : ParseFloat(real), ParseFloat(imag));
10331032
} else {
10341033
throw ExnMalformed();
10351034
}
1036-
} catch (OverflowException) {
1037-
throw PythonOps.ValueError("complex() literal too large to convert");
10381035
} catch {
10391036
throw ExnMalformed();
10401037
}

Tests/test_complex.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
# The .NET Foundation licenses this file to you under the Apache 2.0 License.
33
# See the LICENSE file in the project root for more information.
44

5-
from iptest.type_util import *
6-
from iptest import IronPythonTestCase, run_test
5+
from iptest import big, IronPythonTestCase, mycomplex, run_test
76

87
class ComplexTest(IronPythonTestCase):
98

@@ -156,6 +155,15 @@ def __index__(self):
156155
self.assertEqual(complex(int_with_float(2)), 1+0j)
157156
self.assertEqual(complex(int_with_index(2)), 2+0j)
158157

158+
# .NET allows trailing null characters in its double.Parse but Python doesn't
159+
self.assertRaisesMessage(ValueError, "complex() arg is a malformed string", complex, "\x00")
160+
self.assertRaisesMessage(ValueError, "complex() arg is a malformed string", complex, "1\x00")
161+
self.assertRaisesMessage(ValueError, "complex() arg is a malformed string", complex, "1\x00j")
162+
self.assertRaisesMessage(ValueError, "complex() arg is a malformed string", complex, "1e1\x00")
163+
self.assertRaisesMessage(ValueError, "complex() arg is a malformed string", complex, "1e1\x00j")
164+
self.assertRaisesMessage(ValueError, "complex() arg is a malformed string", complex, "1\x00+1j")
165+
self.assertRaisesMessage(ValueError, "complex() arg is a malformed string", complex, "1+1\x00j")
166+
159167
def test_negative_zero_repr_str(self):
160168
# see also similar test in StdLib
161169

@@ -237,19 +245,15 @@ def test_misc(self):
237245
self.assertEqual(complex(2), complex(2, 0))
238246

239247
def test_inherit(self):
240-
class mycomplex(complex): pass
241-
242248
a = mycomplex(2+1j)
243249
self.assertEqual(a.real, 2)
244250
self.assertEqual(a.imag, 1)
245251

246-
247252
def test_repr(self):
248253
self.assertEqual(repr(1-6j), '(1-6j)')
249254

250-
251255
def test_infinite(self):
252256
self.assertEqual(repr(1.0e340j), 'infj')
253257
self.assertEqual(repr(-1.0e340j),'(-0-infj)')
254258

255-
run_test(__name__)
259+
run_test(__name__)

Tests/test_complex_stdlib.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,7 @@ def load_tests(loader, standard_tests, pattern):
1919
suite.addTest(test.test_complex.ComplexTest('test_abs'))
2020
suite.addTest(test.test_complex.ComplexTest('test_boolcontext'))
2121
suite.addTest(test.test_complex.ComplexTest('test_conjugate'))
22-
if is_netcoreapp and not is_netcoreapp21:
23-
suite.addTest(test.test_complex.ComplexTest('test_constructor'))
24-
else:
25-
suite.addTest(unittest.expectedFailure(test.test_complex.ComplexTest('test_constructor'))) # ValueError: complex() literal too large to convert
22+
suite.addTest(test.test_complex.ComplexTest('test_constructor'))
2623
suite.addTest(test.test_complex.ComplexTest('test_divmod'))
2724
suite.addTest(test.test_complex.ComplexTest('test_file'))
2825
suite.addTest(test.test_complex.ComplexTest('test_floordiv'))
@@ -33,10 +30,7 @@ def load_tests(loader, standard_tests, pattern):
3330
suite.addTest(test.test_complex.ComplexTest('test_neg'))
3431
suite.addTest(test.test_complex.ComplexTest('test_negated_imaginary_literal'))
3532
suite.addTest(test.test_complex.ComplexTest('test_negative_zero_repr_str'))
36-
if is_netcoreapp and not is_netcoreapp21:
37-
suite.addTest(test.test_complex.ComplexTest('test_overflow'))
38-
else:
39-
suite.addTest(unittest.expectedFailure(test.test_complex.ComplexTest('test_overflow'))) # ValueError: complex() literal too large to convert
33+
suite.addTest(test.test_complex.ComplexTest('test_overflow'))
4034
suite.addTest(test.test_complex.ComplexTest('test_plus_minus_0j'))
4135
suite.addTest(test.test_complex.ComplexTest('test_pow'))
4236
suite.addTest(test.test_complex.ComplexTest('test_repr_roundtrip'))

0 commit comments

Comments
 (0)