Menu

#805 SWIG_AsVal_int doesn't report overflow correctly

None
closed-fixed
python (260)
5
2022-03-18
2007-03-28
No

I am upgrading my software (Maya 3D) to use SWIG 1.3.31 from an older version. Some of my unit tests are failing after the upgrade because SWIG is now throwing RuntimeErrors where it used to throw OverflowErrors. SWIG should be throwing Overflow errors my test.

The problem is in SWIG_AsVal_int. It is doing this:

---
long v;
int res = SWIG_AsVal_long (obj, &v);
if (SWIG_IsOK(res)) {
if ((v < INT_MIN || v > INT_MAX)) {
return SWIG_OverflowError;
} else {
---

The problem is that on 32-bit systems, and Windows 64-bit, a long and an int are the same type. So, we never get to the overflow check because the value in "res" is going to be false if the Python number is to big.

And SWIG_AsVal_long does not correctly report the overflow either.

The number I am using in my test is 4000000000, which will fit in an unsigned int, but not a regular int...

Cheers!

Discussion

  • Olly Betts

    Olly Betts - 2022-03-18
    • Description has changed:

    Diff:

    --- old
    +++ new
    @@ -1,4 +1,3 @@
    -
     I am upgrading my software \(Maya 3D\) to use SWIG 1.3.31 from an older version.  Some of my unit tests are failing after the upgrade because SWIG is now throwing RuntimeErrors where it used to throw OverflowErrors.  SWIG should be throwing Overflow errors my test.
    
     The problem is in SWIG\_AsVal\_int.  It is doing this:
    
    • status: open --> closed-fixed
    • Group: -->
     
  • Olly Betts

    Olly Betts - 2022-03-18

    SWIG_AsVal_long() does now check for overflow:

    SWIGINTERN int
    SWIG_AsVal_long (PyObject *obj, long* val)    
    {
    #if PY_VERSION_HEX < 0x03000000
      if (PyInt_Check(obj)) {  
        if (val) *val = PyInt_AsLong(obj);    
        return SWIG_OK;
      } else
    #endif
      if (PyLong_Check(obj)) {
        long v = PyLong_AsLong(obj);
        if (!PyErr_Occurred()) {
          if (val) *val = v;
          return SWIG_OK;
        } else {
          PyErr_Clear();    
          return SWIG_OverflowError;
        } 
      }
      #ifdef SWIG_PYTHON_CAST_MODE
      {
        int dispatch = 0;
        long v = PyInt_AsLong(obj);
        if (!PyErr_Occurred()) {
          if (val) *val = v;
          return SWIG_AddCast(SWIG_OK);
        } else {
          PyErr_Clear();
        }
        if (!dispatch) {
          double d;
          int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d));
          if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) {
            if (val) *val = (long)(d);
            return res;
          }
        }
      }
    #endif
      return SWIG_TypeError;
    }
    

    It seems like this was fixed by:

    commit ba01182ec4806667f4b220b88cc7e25da08d834d
    Author: Alec Cooper <ahnolds@gmail.com>
    Date:   Mon Dec 14 22:38:40 2015 -0500
    
        Fixing Python primitive conversions
        Don't mistakenly treat PyLong objects as PyInt objects in Python3.
          This resolves issues of large integers being incorrectly treated as -1 while also having
          an OverflowError set internally for converting PyLong->long and PyLong->double
        Conversions from PyLong to long, unsigned long, long long, and unsigned long long now
        raise OverflowError rather than TypeError when given an out of range value.
        Removing unnecessary check for PyLong_AsLong when converting PyLong->unsigned long since the
        call to PyLong_AsUnsignedLong will have covered this case.
    
    diff --git a/Lib/python/pyprimtypes.swg b/Lib/python/pyprimtypes.swg
    index 30bb64f6..73a97bc5 100644
    --- a/Lib/python/pyprimtypes.swg
    +++ b/Lib/python/pyprimtypes.swg
    @@ -75,16 +75,20 @@ SWIGINTERNINLINE PyObject*
     SWIGINTERN int
     SWIG_AsVal_dec(long)(PyObject *obj, long* val)
     {
    +%#if PY_VERSION_HEX < 0x03000000
       if (PyInt_Check(obj)) {
         if (val) *val = PyInt_AsLong(obj);
         return SWIG_OK;
    -  } else if (PyLong_Check(obj)) {
    +  } else
    +%#endif
    +  if (PyLong_Check(obj)) {
         long v = PyLong_AsLong(obj);
         if (!PyErr_Occurred()) {
           if (val) *val = v;
           return SWIG_OK;
         } else {
           PyErr_Clear();
    +      return SWIG_OverflowError;
         }
       }
     %#ifdef SWIG_PYTHON_CAST_MODE
    

    [...]

    There isn't a complete testcase here and I don't have access to a 32-bit platform with Python installed either, but based on the above I'm very confident this has been addressed since it was reported. If you can still reproduce please supply a small self-contained testcase and we can reopen.

     

Log in to post a comment.