diff --git a/Lib/test/test_ctypes/test_bitfields.py b/Lib/test/test_ctypes/test_bitfields.py index dad71a0ba7ee4a..af2380e28b8736 100644 --- a/Lib/test/test_ctypes/test_bitfields.py +++ b/Lib/test/test_ctypes/test_bitfields.py @@ -40,8 +40,6 @@ def test_ints(self): setattr(b, name, i) self.assertEqual(getattr(b, name), func(byref(b), name.encode('ascii'))) - # bpo-46913: _ctypes/cfield.c h_get() has an undefined behavior - @support.skip_if_sanitizer(ub=True) def test_shorts(self): b = BITS() name = "M" diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-09-19-06-42-20.gh-issue-96821.jrmhsw.rst b/Misc/NEWS.d/next/Core and Builtins/2022-09-19-06-42-20.gh-issue-96821.jrmhsw.rst new file mode 100644 index 00000000000000..e49e73b1a0bb00 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-09-19-06-42-20.gh-issue-96821.jrmhsw.rst @@ -0,0 +1 @@ +Fix undefined behaviour in ``_ctypes/cfield.c``. diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 13ed8b7eda6555..060ee86fb700fe 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -412,21 +412,23 @@ get_ulonglong(PyObject *v, unsigned long long *p) #define NUM_BITS(x) ((x) >> 16) /* Doesn't work if NUM_BITS(size) == 0, but it never happens in SET() call. */ -#define BIT_MASK(type, size) (((((type)1 << (NUM_BITS(size) - 1)) - 1) << 1) + 1) +#define BIT_MASK(type, size) \ + (assert(NUM_BITS(size) > 0), \ + (((((type)1 << (NUM_BITS(size) - 1)) - 1) << 1) + 1)) /* This macro CHANGES the first parameter IN PLACE. For proper sign handling, we must first shift left, then right. */ #define GET_BITFIELD(v, size) \ if (NUM_BITS(size)) { \ - v <<= (sizeof(v)*8 - LOW_BIT(size) - NUM_BITS(size)); \ + v *= 1ULL << (sizeof(v)*8 - LOW_BIT(size) - NUM_BITS(size)); \ v >>= (sizeof(v)*8 - NUM_BITS(size)); \ } /* This macro RETURNS the first parameter with the bit field CHANGED. */ #define SET(type, x, v, size) \ (NUM_BITS(size) ? \ - ( ( (type)x & ~(BIT_MASK(type, size) << LOW_BIT(size)) ) | ( ((type)v & BIT_MASK(type, size)) << LOW_BIT(size) ) ) \ + (type) ( ( (type)x & ~(BIT_MASK(type, size) * (1ULL << LOW_BIT(size))) ) | ( ((type)v & BIT_MASK(type, size)) * (1ULL << LOW_BIT(size) )) ) \ : (type)v) #if SIZEOF_SHORT == 2