From d7073e51fa286565e76a30c66a20e4507009e372 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 1 Apr 2026 05:45:53 +0300 Subject: [PATCH 1/5] gh-143050: add helper _PyLong_Init() With this we can assume, that _PyLong_Set*() operate on non-immortal integers. --- Include/internal/pycore_long.h | 17 +++++++++++++++++ Objects/longobject.c | 29 +++++++++++++++++------------ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 5ef9cc410e4ebe..709ec454ba72f7 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -285,6 +285,21 @@ _PyLong_SameSign(const PyLongObject *a, const PyLongObject *b) return (a->long_value.lv_tag & SIGN_MASK) == (b->long_value.lv_tag & SIGN_MASK); } +/* Initialize a freshly-allocated int. + * + * Fast operations for single digit integers (including zero) + * assume that there is always at least one digit present. + * The digit has to be initialized explicitly to avoid + * use-of-uninitialized-value. + */ +static inline void +_PyLong_Init(PyLongObject *op) +{ + assert(PyLong_Check(op)); + op->long_value.lv_tag = 1; /* non-immortal zero */ + op->long_value.ob_digit[0] = 0; +} + #define TAG_FROM_SIGN_AND_SIZE(sign, size) \ ((uintptr_t)(1 - (sign)) | ((uintptr_t)(size) << NON_SIZE_BITS)) @@ -294,6 +309,7 @@ _PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) assert(size >= 0); assert(-1 <= sign && sign <= 1); assert(sign != 0 || size == 0); + assert(!_PyLong_IsSmallInt(op)); op->long_value.lv_tag = TAG_FROM_SIGN_AND_SIZE(sign, size); } @@ -301,6 +317,7 @@ static inline void _PyLong_SetDigitCount(PyLongObject *op, Py_ssize_t size) { assert(size >= 0); + assert(!_PyLong_IsSmallInt(op)); op->long_value.lv_tag = (((size_t)size) << NON_SIZE_BITS) | (op->long_value.lv_tag & SIGN_MASK); } diff --git a/Objects/longobject.c b/Objects/longobject.c index d416fc1747ecac..356fffa0b13ba8 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -185,11 +185,9 @@ long_alloc(Py_ssize_t size) return NULL; } _PyObject_Init((PyObject*)result, &PyLong_Type); + _PyLong_Init(result); } _PyLong_SetSignAndDigitCount(result, size != 0, size); - /* The digit has to be initialized explicitly to avoid - * use-of-uninitialized-value. */ - result->long_value.ob_digit[0] = 0; return result; } @@ -258,6 +256,7 @@ _PyLong_FromMedium(sdigit x) return NULL; } _PyObject_Init((PyObject*)v, &PyLong_Type); + _PyLong_Init(v); } digit abs_x = x < 0 ? -x : x; _PyLong_SetSignAndDigitCount(v, x<0?-1:1, 1); @@ -337,6 +336,7 @@ medium_from_stwodigits(stwodigits x) return PyStackRef_NULL; } _PyObject_Init((PyObject*)v, &PyLong_Type); + _PyLong_Init(v); } digit abs_x = x < 0 ? (digit)(-x) : (digit)x; _PyLong_SetSignAndDigitCount(v, x<0?-1:1, 1); @@ -6011,29 +6011,34 @@ static PyObject * long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase) { PyLongObject *tmp, *newobj; - Py_ssize_t i, n; + Py_ssize_t size, ndigits; + int sign; assert(PyType_IsSubtype(type, &PyLong_Type)); tmp = (PyLongObject *)long_new_impl(&PyLong_Type, x, obase); if (tmp == NULL) return NULL; assert(PyLong_Check(tmp)); - n = _PyLong_DigitCount(tmp); + size = _PyLong_DigitCount(tmp); /* Fast operations for single digit integers (including zero) * assume that there is always at least one digit present. */ - if (n == 0) { - n = 1; - } - newobj = (PyLongObject *)type->tp_alloc(type, n); + ndigits = size ? size : 1; + newobj = (PyLongObject *)type->tp_alloc(type, ndigits); if (newobj == NULL) { Py_DECREF(tmp); return NULL; } assert(PyLong_Check(newobj)); - newobj->long_value.lv_tag = tmp->long_value.lv_tag & ~IMMORTALITY_BIT_MASK; - for (i = 0; i < n; i++) { - newobj->long_value.ob_digit[i] = tmp->long_value.ob_digit[i]; + if (_PyLong_IsCompact(tmp)) { + sign = _PyLong_CompactSign(tmp); + } + else { + sign = _PyLong_NonCompactSign(tmp); } + _PyLong_Init(newobj); + _PyLong_SetSignAndDigitCount(newobj, sign, size); + memcpy(newobj->long_value.ob_digit, tmp->long_value.ob_digit, + ndigits * sizeof(digit)); Py_DECREF(tmp); return (PyObject *)newobj; } From ddd329fc43b843dc56f5221ab4de0fd65cce4793 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 1 Apr 2026 13:16:56 +0300 Subject: [PATCH 2/5] address review: rename function to _PyLong_InitTag --- Include/internal/pycore_long.h | 2 +- Objects/longobject.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 709ec454ba72f7..49ac65de976623 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -293,7 +293,7 @@ _PyLong_SameSign(const PyLongObject *a, const PyLongObject *b) * use-of-uninitialized-value. */ static inline void -_PyLong_Init(PyLongObject *op) +_PyLong_InitTag(PyLongObject *op) { assert(PyLong_Check(op)); op->long_value.lv_tag = 1; /* non-immortal zero */ diff --git a/Objects/longobject.c b/Objects/longobject.c index 356fffa0b13ba8..b74a435b541861 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -185,7 +185,7 @@ long_alloc(Py_ssize_t size) return NULL; } _PyObject_Init((PyObject*)result, &PyLong_Type); - _PyLong_Init(result); + _PyLong_InitTag(result); } _PyLong_SetSignAndDigitCount(result, size != 0, size); return result; @@ -256,7 +256,7 @@ _PyLong_FromMedium(sdigit x) return NULL; } _PyObject_Init((PyObject*)v, &PyLong_Type); - _PyLong_Init(v); + _PyLong_InitTag(v); } digit abs_x = x < 0 ? -x : x; _PyLong_SetSignAndDigitCount(v, x<0?-1:1, 1); @@ -336,7 +336,7 @@ medium_from_stwodigits(stwodigits x) return PyStackRef_NULL; } _PyObject_Init((PyObject*)v, &PyLong_Type); - _PyLong_Init(v); + _PyLong_InitTag(v); } digit abs_x = x < 0 ? (digit)(-x) : (digit)x; _PyLong_SetSignAndDigitCount(v, x<0?-1:1, 1); @@ -6035,7 +6035,7 @@ long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase) else { sign = _PyLong_NonCompactSign(tmp); } - _PyLong_Init(newobj); + _PyLong_InitTag(newobj); _PyLong_SetSignAndDigitCount(newobj, sign, size); memcpy(newobj->long_value.ob_digit, tmp->long_value.ob_digit, ndigits * sizeof(digit)); From f6ab450aeb39d084131f0b26f2e0f127eb122f51 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 1 Apr 2026 13:48:10 +0300 Subject: [PATCH 3/5] address review: skip initialization of digit[0] --- Include/internal/pycore_long.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 49ac65de976623..9bdeb67afd7350 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -285,19 +285,12 @@ _PyLong_SameSign(const PyLongObject *a, const PyLongObject *b) return (a->long_value.lv_tag & SIGN_MASK) == (b->long_value.lv_tag & SIGN_MASK); } -/* Initialize a freshly-allocated int. - * - * Fast operations for single digit integers (including zero) - * assume that there is always at least one digit present. - * The digit has to be initialized explicitly to avoid - * use-of-uninitialized-value. - */ +/* Initialize a freshly-allocated int. */ static inline void _PyLong_InitTag(PyLongObject *op) { assert(PyLong_Check(op)); op->long_value.lv_tag = 1; /* non-immortal zero */ - op->long_value.ob_digit[0] = 0; } #define TAG_FROM_SIGN_AND_SIZE(sign, size) \ From f5df517c55178725570824e6e26d19e1c8a2b846 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 1 Apr 2026 15:28:03 +0300 Subject: [PATCH 4/5] address review: restore comment --- Include/internal/pycore_long.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 9bdeb67afd7350..732134e999c63b 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -285,7 +285,13 @@ _PyLong_SameSign(const PyLongObject *a, const PyLongObject *b) return (a->long_value.lv_tag & SIGN_MASK) == (b->long_value.lv_tag & SIGN_MASK); } -/* Initialize a freshly-allocated int. */ +/* Initialize a freshly-allocated int. + * + * Fast operations for single digit integers (including zero) + * assume that there is always at least one digit present. + * The digit has to be initialized explicitly to avoid + * use-of-uninitialized-value. + */ static inline void _PyLong_InitTag(PyLongObject *op) { From 21c59bb5c426c958750a781351804ede91e2e228 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Wed, 1 Apr 2026 15:29:03 +0300 Subject: [PATCH 5/5] XXX redo _PyLong_IsSmallInt --- Include/internal/pycore_long.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_long.h b/Include/internal/pycore_long.h index 732134e999c63b..1bcc449f075c9a 100644 --- a/Include/internal/pycore_long.h +++ b/Include/internal/pycore_long.h @@ -238,11 +238,12 @@ _PyLong_IsSmallInt(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)); - assert(_Py_IsImmortal(op) || (!is_small_int)); - assert((_PyLong_IsCompact(op) - && _PY_IS_SMALL_INT(_PyLong_CompactValue(op))) - || (!is_small_int)); + if (is_small_int) { + assert(PyLong_CheckExact(op)); + assert(_Py_IsImmortal(op)); + assert((_PyLong_IsCompact(op) + && _PY_IS_SMALL_INT(_PyLong_CompactValue(op)))); + } return is_small_int; }