0

Tested using Python 2.7.13 on 64-bit Linux.

I have a C function inside a shared object. This function takes two ints and returns an int. When I try to pass an integer from the Python side that is larger than INT_MAX but still representable as an unsigned int, I don't get a Python-side exception like I would expect.

// add.c
int add(int a, int b)
{
    return a + b;
}

This file is compiled into a shared objects using the following options. (snippet from makefile)

gcc -shared -fPIC add.c -o add.so

I'm using the following python script test_add.c to test the code. I'm probably doing more work than necessary, manually supplying argument types of ctypes.c_int and manually coercing the integers generated by hypothesis, but it doesn't seem like any of those operations should be causing the behavior I'm seeing.

Here's the main test that I'm trying with different values of x and y. One example of a failing test case is x=0 and y=2147483648, a number one larger than INT_MAX.

try:
    s = add(ctypes.c_int(x), ctypes.c_int(y))
except Exception:
    return

# if no exception was thrown, python and C should agree
# about the result of the addition
assert s == (x + y)

And here's the script in full for the sake of completeness:

import ctypes
import os.path
import unittest

from hypothesis import given
import hypothesis.strategies as st

# import shared object from this directory
addso_path = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + "add.so"
addso = ctypes.cdll.LoadLibrary(addso_path)

# extract add symbol, explicitly force its argument types to be
# (int, int)
# and return type to be
# int
add = addso.add
add.argtypes = [ctypes.c_int, ctypes.c_int]
add.restype = ctypes.c_int

class TestAddition(unittest.TestCase):
    @given(x=st.integers(), y=st.integers())
    def test_add(self, x, y):
        # if we catch any error on the python side,
        # then that counts as successfully preventing an
        # out-of-bounds integer before it is passed to C
        try:
            s = add(ctypes.c_int(x), ctypes.c_int(y))
        except Exception:
            return

        # if no exception was thrown, python and C should
        # agree about the result of the addition
        assert s == (x + y)


if __name__ == "__main__":
    unittest.main()

Here's the output of the test suite:

Falsifying example: test_add(self=<__main__.TestAddition testMethod=test_add>, x=0, y=2147483648)
F
======================================================================
FAIL: test_add (__main__.TestAddition)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_add.py", line 22, in test_add
    def test_add(self, x, y):
  File "/.../opt/c-add/venv/lib/python2.7/site-packages/hypothesis/core.py", line 525, in wrapped_test
    print_example=True, is_final=True
  File "/...opt/c-add/venv/lib/python2.7/site-packages/hypothesis/executors.py", line 58, in default_new_style_executor
    return function(data)
  File "/.../opt/c-add/venv/lib/python2.7/site-packages/hypothesis/core.py", line 112, in run
    return test(*args, **kwargs)
  File "test_add.py", line 33, in test_add
    assert s == (x + y)
AssertionError
Greg Nisbet
  • 6,710
  • 3
  • 25
  • 65
  • Note that the [documentaiton for ctypes.c_int states](https://docs.python.org/2/library/ctypes.html#ctypes.c_int): "no overflow checking is done". Thus, your (input) long will be silently "converted" (truncated, 2-complement wrapped, whatever) to an int, and the addition in C will carry on from there. –  Mar 27 '17 at 00:01
  • See also http://stackoverflow.com/q/7770949/1256452 - as @Evert says, `ctypes` does not do overflow checking itself. To get the bit (and hence byte) size of a ctypes type, see http://stackoverflow.com/q/27213647/1256452 – torek Mar 27 '17 at 00:20
  • @torek, is there a deep reason why ctypes can't portably check for out-of-bounds Python numbers when converting to a ctype (like `ctype.c_int`) or does it simply not check? – Greg Nisbet Mar 27 '17 at 01:54
  • There is, as far as I can tell, no reason it *could* not, it just *does* not. – torek Mar 27 '17 at 03:36

0 Answers0