Skip to content

Commit

Permalink
[2.1.x] Fixed CVE-2019-6975 -- Fixed memory exhaustion in utils.numbe…
Browse files Browse the repository at this point in the history
…rformat.format().

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

Backport of 402c0ca from master
  • Loading branch information
carltongibson committed Feb 11, 2019
1 parent 657bbb1 commit 40cd190
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 1 deletion.
15 changes: 14 additions & 1 deletion django/utils/numberformat.py
Expand Up @@ -27,7 +27,20 @@ def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='',
# sign # sign
sign = '' sign = ''
if isinstance(number, Decimal): 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, use_l10n,
)
return '{}e{}'.format(coefficient, exponent)
else:
str_number = '{:f}'.format(number)
else: else:
str_number = str(number) str_number = str(number)
if str_number[0] == '-': if str_number[0] == '-':
Expand Down
12 changes: 12 additions & 0 deletions docs/releases/1.11.19.txt
Expand Up @@ -5,3 +5,15 @@ Django 1.11.19 release notes
*February 11, 2019* *February 11, 2019*


Django 1.11.19 fixes a security issue in 1.11.18. Django 1.11.19 fixes a security issue in 1.11.18.

CVE-2019-6975: Memory exhaustion in ``django.utils.numberformat.format()``
--------------------------------------------------------------------------

If ``django.utils.numberformat.format()`` -- used by ``contrib.admin`` as well
as the the ``floatformat``, ``filesizeformat``, and ``intcomma`` templates
filters -- received a ``Decimal`` with a large number of digits or a large
exponent, it could lead to significant memory usage due to a call to
``'{:f}'.format()``.

To avoid this, decimals with more than 200 digits are now formatted using
scientific notation.
12 changes: 12 additions & 0 deletions docs/releases/2.0.11.txt
Expand Up @@ -5,3 +5,15 @@ Django 2.0.11 release notes
*February 11, 2019* *February 11, 2019*


Django 2.0.11 fixes a security issue in 2.0.10. Django 2.0.11 fixes a security issue in 2.0.10.

CVE-2019-6975: Memory exhaustion in ``django.utils.numberformat.format()``
--------------------------------------------------------------------------

If ``django.utils.numberformat.format()`` -- used by ``contrib.admin`` as well
as the the ``floatformat``, ``filesizeformat``, and ``intcomma`` templates
filters -- received a ``Decimal`` with a large number of digits or a large
exponent, it could lead to significant memory usage due to a call to
``'{:f}'.format()``.

To avoid this, decimals with more than 200 digits are now formatted using
scientific notation.
12 changes: 12 additions & 0 deletions docs/releases/2.1.6.txt
Expand Up @@ -6,6 +6,18 @@ Django 2.1.6 release notes


Django 2.1.6 fixes a security issue and a bug in 2.1.5. Django 2.1.6 fixes a security issue and a bug in 2.1.5.


CVE-2019-6975: Memory exhaustion in ``django.utils.numberformat.format()``
--------------------------------------------------------------------------

If ``django.utils.numberformat.format()`` -- used by ``contrib.admin`` as well
as the the ``floatformat``, ``filesizeformat``, and ``intcomma`` templates
filters -- received a ``Decimal`` with a large number of digits or a large
exponent, it could lead to significant memory usage due to a call to
``'{:f}'.format()``.

To avoid this, decimals with more than 200 digits are now formatted using
scientific notation.

Bugfixes Bugfixes
======== ========


Expand Down
19 changes: 19 additions & 0 deletions tests/utils_tests/test_numberformat.py
Expand Up @@ -80,6 +80,25 @@ def test_decimal_numbers(self):
) )
self.assertEqual(nformat(Decimal('3.'), '.'), '3') self.assertEqual(nformat(Decimal('3.'), '.'), '3')
self.assertEqual(nformat(Decimal('3.0'), '.'), '3.0') self.assertEqual(nformat(Decimal('3.0'), '.'), '3.0')
# 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:
with self.subTest(value=value):
self.assertEqual(nformat(Decimal(value), '.', decimal_pos), expected_value)


def test_decimal_subclass(self): def test_decimal_subclass(self):
class EuroDecimal(Decimal): class EuroDecimal(Decimal):
Expand Down

0 comments on commit 40cd190

Please sign in to comment.