mirror of
https://github.com/python/cpython.git
synced 2026-04-13 15:20:52 +00:00
gh-143050: Correct PyLong_FromString() to use _PyLong_Negate() (#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(). Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
parent
829e4d0b14
commit
db5936c5b8
4 changed files with 34 additions and 18 deletions
|
|
@ -232,6 +232,20 @@ _PyLong_IsPositive(const PyLongObject *op)
|
|||
return (op->long_value.lv_tag & SIGN_MASK) == 0;
|
||||
}
|
||||
|
||||
/* Return true if the argument is a small int */
|
||||
static inline bool
|
||||
_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));
|
||||
return is_small_int;
|
||||
}
|
||||
|
||||
static inline Py_ssize_t
|
||||
_PyLong_DigitCount(const PyLongObject *op)
|
||||
{
|
||||
|
|
@ -293,7 +307,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_IsSmallInt(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;
|
||||
|
|
|
|||
|
|
@ -803,6 +803,16 @@ 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):
|
||||
# Bug coming from using _pylong.int_from_string(), that
|
||||
# currently requires > 6000 decimal digits.
|
||||
int('-' + '0' * 7000, 10)
|
||||
_testcapi.test_immortal_small_ints()
|
||||
# Test also nonzero small int
|
||||
int('-' + '0' * 7000 + '123', 10)
|
||||
_testcapi.test_immortal_small_ints()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -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_IsSmallInt((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_IsSmallInt((PyLongObject *)obj);
|
||||
assert(!has_int_immortal_bit);
|
||||
Py_DECREF(obj);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3119,11 +3119,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;
|
||||
|
|
@ -3623,21 +3623,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_IsSmallInt((PyLongObject *)self)) {
|
||||
// See PEP 683, section Accidental De-Immortalizing for details
|
||||
_Py_SetImmortal(self);
|
||||
return;
|
||||
|
|
@ -3652,7 +3642,7 @@ _PyLong_ExactDealloc(PyObject *self)
|
|||
static void
|
||||
long_dealloc(PyObject *self)
|
||||
{
|
||||
if (_long_is_small_int(self)) {
|
||||
if (_PyLong_IsSmallInt((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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue