Skip to content

Commit ced1164

Browse files
skirpichevvstinner
authored andcommitted
gh-143050: Correct PyLong_FromString() to use _PyLong_Negate() (GH-145901)
The long_from_string_base() might return a small integer, when the _pylong.py is used to do conversion. Hence, we must be careful here to not smash it "small int" bit by using the _PyLong_FlipSign(). (cherry picked from commit db5936c) Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com> Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent e387bac commit ced1164

File tree

4 files changed

+34
-18
lines changed

4 files changed

+34
-18
lines changed

Include/internal/pycore_long.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,20 @@ _PyLong_IsPositive(const PyLongObject *op)
233233
return (op->long_value.lv_tag & SIGN_MASK) == 0;
234234
}
235235

236+
/* Return true if the argument is a small int */
237+
static inline bool
238+
_PyLong_IsSmallInt(const PyLongObject *op)
239+
{
240+
assert(PyLong_Check(op));
241+
bool is_small_int = (op->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0;
242+
assert(PyLong_CheckExact(op) || (!is_small_int));
243+
assert(_Py_IsImmortal(op) || (!is_small_int));
244+
assert((_PyLong_IsCompact(op)
245+
&& _PY_IS_SMALL_INT(_PyLong_CompactValue(op)))
246+
|| (!is_small_int));
247+
return is_small_int;
248+
}
249+
236250
static inline Py_ssize_t
237251
_PyLong_DigitCount(const PyLongObject *op)
238252
{
@@ -294,7 +308,9 @@ _PyLong_SetDigitCount(PyLongObject *op, Py_ssize_t size)
294308
#define NON_SIZE_MASK ~(uintptr_t)((1 << NON_SIZE_BITS) - 1)
295309

296310
static inline void
297-
_PyLong_FlipSign(PyLongObject *op) {
311+
_PyLong_FlipSign(PyLongObject *op)
312+
{
313+
assert(!_PyLong_IsSmallInt(op));
298314
unsigned int flipped_sign = 2 - (op->long_value.lv_tag & SIGN_MASK);
299315
op->long_value.lv_tag &= NON_SIZE_MASK;
300316
op->long_value.lv_tag |= flipped_sign;

Lib/test/test_capi/test_long.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,16 @@ def to_digits(num):
803803
self.assertEqual(pylongwriter_create(negative, digits), num,
804804
(negative, digits))
805805

806+
def test_bug_143050(self):
807+
with support.adjust_int_max_str_digits(0):
808+
# Bug coming from using _pylong.int_from_string(), that
809+
# currently requires > 6000 decimal digits.
810+
int('-' + '0' * 7000, 10)
811+
_testcapi.test_immortal_small_ints()
812+
# Test also nonzero small int
813+
int('-' + '0' * 7000 + '123', 10)
814+
_testcapi.test_immortal_small_ints()
815+
806816

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

Modules/_testcapi/immortal.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored))
3131
for (int i = -5; i <= 256; i++) {
3232
PyObject *obj = PyLong_FromLong(i);
3333
assert(verify_immortality(obj));
34-
int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK;
34+
int has_int_immortal_bit = _PyLong_IsSmallInt((PyLongObject *)obj);
3535
assert(has_int_immortal_bit);
3636
}
3737
for (int i = 257; i <= 260; i++) {
3838
PyObject *obj = PyLong_FromLong(i);
3939
assert(obj);
40-
int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK;
40+
int has_int_immortal_bit = _PyLong_IsSmallInt((PyLongObject *)obj);
4141
assert(!has_int_immortal_bit);
4242
Py_DECREF(obj);
4343
}

Objects/longobject.c

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3047,11 +3047,11 @@ PyLong_FromString(const char *str, char **pend, int base)
30473047
}
30483048

30493049
/* Set sign and normalize */
3050-
if (sign < 0) {
3051-
_PyLong_FlipSign(z);
3052-
}
30533050
long_normalize(z);
30543051
z = maybe_small_long(z);
3052+
if (sign < 0) {
3053+
_PyLong_Negate(&z);
3054+
}
30553055

30563056
if (pend != NULL) {
30573057
*pend = (char *)str;
@@ -3551,21 +3551,11 @@ long_richcompare(PyObject *self, PyObject *other, int op)
35513551
Py_RETURN_RICHCOMPARE(result, 0, op);
35523552
}
35533553

3554-
static inline int
3555-
/// Return 1 if the object is one of the immortal small ints
3556-
_long_is_small_int(PyObject *op)
3557-
{
3558-
PyLongObject *long_object = (PyLongObject *)op;
3559-
int is_small_int = (long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0;
3560-
assert((!is_small_int) || PyLong_CheckExact(op));
3561-
return is_small_int;
3562-
}
3563-
35643554
void
35653555
_PyLong_ExactDealloc(PyObject *self)
35663556
{
35673557
assert(PyLong_CheckExact(self));
3568-
if (_long_is_small_int(self)) {
3558+
if (_PyLong_IsSmallInt((PyLongObject *)self)) {
35693559
// See PEP 683, section Accidental De-Immortalizing for details
35703560
_Py_SetImmortal(self);
35713561
return;
@@ -3580,7 +3570,7 @@ _PyLong_ExactDealloc(PyObject *self)
35803570
static void
35813571
long_dealloc(PyObject *self)
35823572
{
3583-
if (_long_is_small_int(self)) {
3573+
if (_PyLong_IsSmallInt((PyLongObject *)self)) {
35843574
/* This should never get called, but we also don't want to SEGV if
35853575
* we accidentally decref small Ints out of existence. Instead,
35863576
* since small Ints are immortal, re-set the reference count.

0 commit comments

Comments
 (0)