File CVE-2019-6975.patch of Package python-Django
From 0bbb560183fabf0533289700845dafa94951f227 Mon Sep 17 00:00:00 2001
From: Carlton Gibson <carlton.gibson@noumenal.es>
Date: Mon, 11 Feb 2019 11:15:45 +0100
Subject: [PATCH] [1.11.x] 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 ++++++++++++++-
docs/releases/1.11.19.txt | 12 ++++++++++++
tests/utils_tests/test_numberformat.py | 18 ++++++++++++++++++
3 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/django/utils/numberformat.py b/django/utils/numberformat.py
index ae5a3b547410..97d112aad2d8 100644
--- a/django/utils/numberformat.py
+++ b/django/utils/numberformat.py
@@ -30,7 +30,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 3dd1b0644ff2..769406c0d896 100644
--- a/tests/utils_tests/test_numberformat.py
+++ b/tests/utils_tests/test_numberformat.py
@@ -59,3 +59,21 @@ def test_decimal_numbers(self):
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)