diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index d545ba0c3abb52..376c55eb09869a 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -231,6 +231,15 @@ _PyLong_IsPositive(const PyLongObject *op) return (op->long_value.lv_tag & SIGN_MASK) == 0; } +static inline bool +_PyLong_IsImmortal(const PyLongObject *op) +{ + assert(PyLong_Check(op)); + bool is_small_int = (op->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0; + assert(PyLong_CheckExact(op) || (!is_small_int)); + return is_small_int; +} + static inline Py_ssize_t _PyLong_DigitCount(const PyLongObject *op) { @@ -292,7 +301,9 @@ _PyLong_SetDigitCount(PyLongObject *op, Py_ssize_t size) #define NON_SIZE_MASK ~(uintptr_t)((1 << NON_SIZE_BITS) - 1) static inline void -_PyLong_FlipSign(PyLongObject *op) { +_PyLong_FlipSign(PyLongObject *op) +{ + assert(!_PyLong_IsImmortal(op)); unsigned int flipped_sign = 2 - (op->long_value.lv_tag & SIGN_MASK); op->long_value.lv_tag &= NON_SIZE_MASK; op->long_value.lv_tag |= flipped_sign; diff --git a/Lib/test/test_capi/test_long.py b/Lib/test/test_capi/test_long.py index d3156645eeec2d..adb5b545d52b48 100644 --- a/Lib/test/test_capi/test_long.py +++ b/Lib/test/test_capi/test_long.py @@ -803,6 +803,13 @@ def to_digits(num): self.assertEqual(pylongwriter_create(negative, digits), num, (negative, digits)) + def test_bug_143050(self): + with support.adjust_int_max_str_digits(0): + int('-' + '0' * 7000, 10) + _testcapi.test_immortal_small_ints() + int('-' + '0' * 7000 + '123', 10) + _testcapi.test_immortal_small_ints() + if __name__ == "__main__": unittest.main() diff --git a/Modules/_testcapi/immortal.c b/Modules/_testcapi/immortal.c index af510cab655356..21c6cdfb9a2cb2 100644 --- a/Modules/_testcapi/immortal.c +++ b/Modules/_testcapi/immortal.c @@ -31,13 +31,13 @@ test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored)) for (int i = -5; i <= 1024; i++) { PyObject *obj = PyLong_FromLong(i); assert(verify_immortality(obj)); - int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK; + int has_int_immortal_bit = _PyLong_IsImmortal((PyLongObject *)obj); assert(has_int_immortal_bit); } for (int i = 1025; i <= 1030; i++) { PyObject *obj = PyLong_FromLong(i); assert(obj); - int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK; + int has_int_immortal_bit = _PyLong_IsImmortal((PyLongObject *)obj); assert(!has_int_immortal_bit); Py_DECREF(obj); } diff --git a/Objects/longobject.c b/Objects/longobject.c index 7ce5d0535b884e..35f17198ee1264 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -3118,11 +3118,11 @@ PyLong_FromString(const char *str, char **pend, int base) } /* Set sign and normalize */ - if (sign < 0) { - _PyLong_FlipSign(z); - } long_normalize(z); z = maybe_small_long(z); + if (sign < 0) { + _PyLong_Negate(&z); + } if (pend != NULL) { *pend = (char *)str; @@ -3622,21 +3622,11 @@ long_richcompare(PyObject *self, PyObject *other, int op) Py_RETURN_RICHCOMPARE(result, 0, op); } -static inline int -/// Return 1 if the object is one of the immortal small ints -_long_is_small_int(PyObject *op) -{ - PyLongObject *long_object = (PyLongObject *)op; - int is_small_int = (long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0; - assert((!is_small_int) || PyLong_CheckExact(op)); - return is_small_int; -} - void _PyLong_ExactDealloc(PyObject *self) { assert(PyLong_CheckExact(self)); - if (_long_is_small_int(self)) { + if (_PyLong_IsImmortal((PyLongObject *)self)) { // See PEP 683, section Accidental De-Immortalizing for details _Py_SetImmortal(self); return; @@ -3651,7 +3641,7 @@ _PyLong_ExactDealloc(PyObject *self) static void long_dealloc(PyObject *self) { - if (_long_is_small_int(self)) { + if (_PyLong_IsImmortal((PyLongObject *)self)) { /* This should never get called, but we also don't want to SEGV if * we accidentally decref small Ints out of existence. Instead, * since small Ints are immortal, re-set the reference count.