77#include < functional>
88#include < stdexcept>
99#include < string>
10+ #include < climits>
11+ #include < cstdint>
12+
13+ // PyLong_AsInt entered the public/stable CPython API in 3.13. cuda.bindings
14+ // supports Python 3.10+, so provide a file-local backport for older builds.
15+ // This is a copy of the CPython implementation; it is `static` (unlike the
16+ // original) because this header is compiled into every extension module that
17+ // includes it, mirroring the other helpers below.
18+ #if PY_VERSION_HEX < 0x030D0000
19+ static int
20+ PyLong_AsInt (PyObject *obj)
21+ {
22+ int overflow;
23+ long result = PyLong_AsLongAndOverflow (obj, &overflow);
24+ if (overflow || result > INT_MAX || result < INT_MIN ) {
25+ PyErr_SetString (PyExc_OverflowError,
26+ " Python int too large to convert to C int" );
27+ return -1 ;
28+ }
29+ return (int )result;
30+ }
31+ #endif
1032
1133static PyObject* ctypes_module = nullptr ;
1234
@@ -69,7 +91,13 @@ static void populate_feeders(PyTypeObject* target_t, PyTypeObject* source_t)
6991 {
7092 m_feeders[{target_t ,source_t }] = [](void * ptr, PyObject* value) -> int
7193 {
72- *((int *)ptr) = (int )PyLong_AsLong (value);
94+ // PyLong_AsInt range-checks against the 32-bit int slot and raises
95+ // OverflowError itself, so an out-of-range value is rejected rather
96+ // than silently truncated.
97+ int v = PyLong_AsInt (value);
98+ if (v == -1 && PyErr_Occurred ())
99+ return -1 ;
100+ *((int *)ptr) = v;
73101 return sizeof (int );
74102 };
75103 return ;
@@ -89,7 +117,23 @@ static void populate_feeders(PyTypeObject* target_t, PyTypeObject* source_t)
89117 {
90118 m_feeders[{target_t ,source_t }] = [](void * ptr, PyObject* value) -> int
91119 {
92- *((int8_t *)ptr) = (int8_t )PyLong_AsLong (value);
120+ // c_byte is an 8-bit slot with no dedicated CPython converter, so
121+ // range-check explicitly against INT8_MIN/INT8_MAX. AsLongAndOverflow's
122+ // `overflow` only flags values outside `long` (64-bit on LP64), so a
123+ // value in that range would be silently truncated by (int8_t)v without
124+ // the explicit bounds check. When overflow!=0, v is the -1 sentinel
125+ // (not the real value), so that case must be caught before trusting v.
126+ int overflow = 0 ;
127+ long v = PyLong_AsLongAndOverflow (value, &overflow);
128+ if (overflow == 0 && v == -1 && PyErr_Occurred ())
129+ return -1 ; // non-overflow conversion error; exception already set
130+ if (overflow != 0 || v < INT8_MIN || v > INT8_MAX )
131+ {
132+ PyErr_SetString (PyExc_OverflowError,
133+ " Python int is out of range for a c_byte (8-bit) kernel argument" );
134+ return -1 ;
135+ }
136+ *((int8_t *)ptr) = (int8_t )v;
93137 return sizeof (int8_t );
94138 };
95139 return ;
0 commit comments