File: 0018-CVE-2019-6975.patch

package info (click to toggle)
python-django 1%3A1.10.7-2%2Bdeb9u9
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 46,768 kB
  • sloc: python: 210,877; javascript: 18,032; xml: 201; makefile: 198; sh: 145
file content (69 lines) | stat: -rw-r--r-- 3,113 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
From: Carlton Gibson <carlton.gibson@noumenal.es>
Date: Mon, 11 Feb 2019 11:15:45 +0100
Subject: Fixed CVE-2019-6975 -- Fixed memory exhaustion in
 utils.numberformat.format().

Thanks Sjoerd Job Postmus for the report and initial patch.
Thanks Michael Manfre, Tim Graham, and Florian Apolloner for review.

Backport of 402c0caa851e265410fbcaa55318f22d2bf22ee2 from master.
---
 django/utils/numberformat.py           | 15 ++++++++++++++-
 tests/utils_tests/test_numberformat.py | 18 ++++++++++++++++++
 2 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/django/utils/numberformat.py b/django/utils/numberformat.py
index 6667d82..8b4d228 100644
--- a/django/utils/numberformat.py
+++ b/django/utils/numberformat.py
@@ -27,7 +27,20 @@ def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='',
     # sign
     sign = ''
     if isinstance(number, Decimal):
-        str_number = '{:f}'.format(number)
+        # Format values with more than 200 digits (an arbitrary cutoff) using
+        # scientific notation to avoid high memory usage in {:f}'.format().
+        _, digits, exponent = number.as_tuple()
+        if abs(exponent) + len(digits) > 200:
+            number = '{:e}'.format(number)
+            coefficient, exponent = number.split('e')
+            # Format the coefficient.
+            coefficient = format(
+                coefficient, decimal_sep, decimal_pos, grouping,
+                thousand_sep, force_grouping,
+            )
+            return '{}e{}'.format(coefficient, exponent)
+        else:
+            str_number = '{:f}'.format(number)
     else:
         str_number = six.text_type(number)
     if str_number[0] == '-':
diff --git a/tests/utils_tests/test_numberformat.py b/tests/utils_tests/test_numberformat.py
index 3dd1b06..769406c 100644
--- a/tests/utils_tests/test_numberformat.py
+++ b/tests/utils_tests/test_numberformat.py
@@ -60,6 +60,24 @@ class TestNumberFormat(TestCase):
         self.assertEqual(nformat(Decimal('1234'), '.', grouping=2, thousand_sep=',', force_grouping=True), '12,34')
         self.assertEqual(nformat(Decimal('-1234.33'), '.', decimal_pos=1), '-1234.3')
         self.assertEqual(nformat(Decimal('0.00000001'), '.', decimal_pos=8), '0.00000001')
+        # Very large & small numbers.
+        tests = [
+            ('9e9999', None, '9e+9999'),
+            ('9e9999', 3, '9.000e+9999'),
+            ('9e201', None, '9e+201'),
+            ('9e200', None, '9e+200'),
+            ('1.2345e999', 2, '1.23e+999'),
+            ('9e-999', None, '9e-999'),
+            ('1e-7', 8, '0.00000010'),
+            ('1e-8', 8, '0.00000001'),
+            ('1e-9', 8, '0.00000000'),
+            ('1e-10', 8, '0.00000000'),
+            ('1e-11', 8, '0.00000000'),
+            ('1' + ('0' * 300), 3, '1.000e+300'),
+            ('0.{}1234'.format('0' * 299), 3, '1.234e-300'),
+        ]
+        for value, decimal_pos, expected_value in tests:
+            self.assertEqual(nformat(Decimal(value), '.', decimal_pos), expected_value)
 
     def test_decimal_subclass(self):
         class EuroDecimal(Decimal):