Is "CWE-190 Integer Overflow or Wraparound" an issue in CPython or not?

I am part of a OpenSSF group writing documentation on secure coding in Python and we are scratching our heads if overflow’s, as per CWE-190, can be an issue or not.

As int in Python can scale with virtually no boundaries it appears nonsense to talk about Overflows. This all change once we we use division that always returns a float.

Float has boundaries that might come unexpected when using int.

>>> foo=2
>>> bar=3
>>> type(foo)
<class 'int'>
>>> type(bar)
<class 'int'>
>>> baz=foo/bar
>>> print(baz)
0.6666666666666666
>>> print(int(baz))
0
>>> type(baz)
<class 'float'>

Control over float precision make us ‘potentially’ use Decimal that also comes with boundaries.

>>> Decimal(0.66666).quantize(Decimal("0"), rounding=ROUND_HALF_UP)
Decimal('1')

If the exponent is greater than Emax or less than Emin there will be an overflow signal. You can look at the fields in decimal.defaultContext to see what they are by default, or decimal.getContext() to see what they are at any given time.

Then there are also ctypes that come with typical C limits.

So do we have overflow issues or not ?

Overall OpenSSF project on secure coding I work on:
https://github.com/ossf/wg-best-practices-os-developers/tree/main/docs/Secure-Coding-Guide-for-Python

Update: added CWE-190 link, added code examples.

The page linked to does not appear to list CWE-190.
Where can I read its definition?

You can read about it on Mitre’s page.
CWE-190

The link in “See also” refers to the documentation on secure coding in Python.

1 Like

There are two types of integers:

Integers (int)

These represent numbers in an unlimited range, subject to available (virtual) memory only. For the purpose of shift and mask operations, a binary representation is assumed, and negative numbers are represented in a variant of 2’s complement which gives the illusion of an infinite string of sign bits extending to the left.

Booleans (bool)

These represent the truth values False and True. The two objects representing the values False and True are the only Boolean objects. The Boolean type is a subtype of the integer type, and Boolean values behave like the values 0 and 1, respectively, in almost all contexts, the exception being that when converted to a string, the strings "False" or "True" are returned, respectively.

I don’t think anyone using ctypes expects guard rails.

I anticipated some loss of precision at the operational limits. But I didn’t expect to construct infinity however:

>>> import sys
>>> sys.float_info
sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1) 
>>> float(sys.float_info.max*10)
inf

inf is a valid float though.

1 Like

My browser search on the original page that this one does not find “see also”.
Where are you seeing that?

So in Python3.14,.0a3 ([MSC v.1942 64 bit (AMD64)] on win32) even though sys.float_max *10 == float('Infinity'), float(int(sys.float_max) * 10) does produce an Overflow error:

>python -c "import sys; print(float(int(sys.float_info.max) * 10))"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import sys; print(float(int(sys.float_info.max) * 10))
                      ~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
OverflowError: int too large to convert to float

So yes it is an issue. Coders perhaps ought to be aware of this quirk. A more pertinent question however is, “is this a concern?”. E.g. is the issue handled gracefully and safely?

Personally I’m not concerned. An OverflowError is self-explanatory, and can easily be caught by code likely to raise it, and mitigated by normal checking of untrusted user inputs.

I’m not an expert on CWEs, but this is my understanding.

  1. CPython’s native ints are immune to CWE-190, being bounded only by the amount of memory available. Your program would crash rather than overflowing an int.
  2. Your point about unexpected int -> float -> int behavior sounds more like CWE-681. Example 2, in particular, is almost exactly the same as the code you gave.
  3. I’m pretty sure that CWE-190 is specific to int, but even if it’s supposed to apply to other types, Decimal still doesn’t fall under it. Overflows in Decimal raise an exception, while the CWE states that the issue is “the logic assumes that the resulting value will always be larger than the original value.” Since there is no “resulting value” for the logic to assume anything about, CWE-190 doesn’t apply. You will want to be aware of how to handle that exception, regardless of whether it’s in CWE or not, of course.
  4. ctypes come with the typical limitations of C types, including CWE-190. In a document teaching about secure Python coding practices, it might be worth talking about the dangers of ctypes.
1 Like

Are you asking whether we have overflow issues that do not raise OverflowError?

1 Like

ctypes are frequently mentioned in relation with optimizing speed and memory for computation intensive tasks. Often explained like a ‘magic trick’. To the non-C coder that appears attractive until there is the realization that usage of raw c like types comes with loads of caveats and house keeping. Especially when combined with multi-threading :smiley:

on your code example:
"infinity", "-infinity" , sys.float_info. max , "NaN" can have some interesting side effects when thrown against an API that does not expect those values :smiley:

Feel free to correct me on the following:
I believe there is a need to warn inexperienced or non-C coders to use ctypes as a primary form of speed optimization. Preferred option should be to choose the right design pattern and profiling. I also assume ctypes don’t play well with multi-threading but never found my self in situation where I was required to use ctypes in Python for such purpose. I am not sure if CWE-190 is the best place for warning about those issues. It appears that multiple CWEs share the same relation with ctypes. So it might better to have a single explanation, with some examples, for them all. Overall we would explain that using ctypes requires secure coding best practice for C.

Well I am specifically trying to figure out if it makes sense to write a rule on CWE-190 due to mentioned issues.

Well yes but I find that in reality there is always a limit to anything computational for the time being. Its just a question of when and how hard we hit them. Those limits get increasingly bigger but still exist.

1 Like

Under closer inspection one could say that the Python type commonly known as ‘int’ is apparently a numbers.Integral and therefore does not qualify for CWE-190. ctype int does qualify but is potentially not worth while writing about in a single base CWE rule. Better to combine CWEs that are applicable for ctypes and refer to using C secure coding best practices.

Could be fluke or just my limitless imagination but I assume that not everyone is aware that Python int does turn int into float on for every division, that is including results that require no floating point representation. My examples was int/int always returning a float.

Yes I agree but am not sure the best and most compact form to communicate that in context with CWEs. We try to have each rule in context with a single base or class CWE. Atm it appears that best course of action is to collect all CWEs that in relation to ctypes and explain them in one rule subsequently breaking our structure.

Could be fluke or just my limitless imagination but I assume that not everyone is aware that Python int does turn int into float on for every division, that is including results that require no floating point representation. My examples was int/int always returning a float.

I agree, people need to be taught that int/int in Python always returns a float, and that they should use floor division, //, if they want an int after division. That seems reasonable to teach in a security context, under CWE-681.

2 Likes

I shan’t correct you any further than saying I disagree, and that such warnings are a complete waste of time.

2 Likes

Hi James,

I have done some digging on this. There are interactions with the operating system causing libpython to throw OverflowErrors.

noncompliant02.py:

# SPDX-FileCopyrightText: OpenSSF project contributors
# SPDX-License-Identifier: MIT
"""Noncompliant Code Example"""

from datetime import datetime, timedelta


def get_datetime(currtime: datetime, hours: int):
    """
    Gets the time n hours in the future or past

    Parameters:
    currtime (datetime): A datetime object with the starting datetime.
    hours (int): Hours going forward or backwards

    Returns:
    datetime: A datetime object
    """
    return currtime + timedelta(hours=hours)


#####################
# attempting to exploit above code example
#####################
datetime.fromtimestamp(0)
currtime = datetime.fromtimestamp(1)  # 1st Jan 1970

# OK values are expected to work
# NOK values trigger OverflowErrors in libpython written in C
hours_list = [
    0,  # OK
    1,  # OK
    70389526,  # OK
    70389527,  # NOK
    51539700001,  # NOK
    24000000001,  # NOK
    -1,  # OK
    -17259889,  # OK
    -17259890,  # NOK
    -23999999999,  # NOK
    -51539699999,  # NOK
]
for hours in hours_list:
    try:
        result = get_datetime(currtime, hours)
        print(f"{hours} OK, datetime='{result}'")
    except Exception as exception:
        print(f"{hours} {repr(exception)}")

The noncompliant02.py code is triggering various OverflowError exceptions in the libpython library:

  • date value out of range
  • OverflowError('Python int too large to convert to C int')
  • days=1000000000; must have magnitude <= 999999999

Can’t tell how much that could be an issue. It would be interesting to know how to find all similar entry points of such variables going through libpython and fuzz them.

Nice work!

I apologise by the way, for previously neglecting to distinguish between a) adding to the sometimes overwhelming and bewildering number of security warnings out there (leading to a cry wolf situation), and b) mentioning a warning about ctypes within the secure Python coding guidance the team is writing anyway. I’ve got no problem whatsoever with the latter, that’s absolutely the right place for such warnings.

This means it is secure, as the value does not become very small or negative.