From cd56987efc84c3bef1b00f2626cea106974e77b6 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" <68491+gpshead@users.noreply.github.com> Date: Wed, 21 Oct 2026 20:50:05 -0700 Subject: [PATCH] gh-160162: Fix crash in int left shift with huge count on 32-bit (GH-160163) Bound the shift count by MAX_LONG_DIGITS*PyLong_SHIFT instead of PY_SSIZE_T_MAX*PyLong_SHIFT, so that the result size computed in long_lshift1() cannot overflow Py_ssize_t. (cherry picked from commit d4750a77519d9936da875953d85e07a851a1d8cd) Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com> --- Lib/test/test_long.py | 10 ++++++++++ .../2026-10-22-00-30-21.gh-issue-160162.lShf32.rst | 3 +++ Objects/longobject.c | 4 +++- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-10-22-00-30-21.gh-issue-160162.lShf32.rst diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index b48a8812a1a2d13..7c368e121024d27 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -996,6 +996,16 @@ def test_huge_lshift(self, size): self.assertEqual(v.bit_count(), 2) self.assertEqual(v >> size, 5) + @support.cpython_only + def test_huge_lshift_overflow(self): + # The number of digits of the result doesn't fit in Py_ssize_t. + for a in 1, -1, 1 << SHIFT, -1 << SHIFT, 1 << 10*SHIFT: + for ndigits in range(sys.maxsize - 12, sys.maxsize + 1): + for shift in ndigits*SHIFT - 1, ndigits*SHIFT: + with self.subTest(a=a, shift=shift): + with self.assertRaises(OverflowError): + a << shift + def test_huge_rshift(self): huge_shift = 1 << 1000 self.assertEqual(42 >> huge_shift, 0) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-22-00-30-21.gh-issue-160162.lShf32.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-22-00-30-21.gh-issue-160162.lShf32.rst new file mode 100644 index 000000000000000..5aa2021bddd44ee --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-22-00-30-21.gh-issue-160162.lShf32.rst @@ -0,0 +1,3 @@ +Fix a crash on 32-bit platforms when left shifting an :class:`int` by a +count close to ``sys.maxsize * sys.int_info.bits_per_digit``. +:exc:`OverflowError` is now raised, as for other too large counts. diff --git a/Objects/longobject.c b/Objects/longobject.c index 5c830cd330acc25..bafded6ced706a8 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -5441,7 +5441,9 @@ long_lshift_int64(PyLongObject *a, int64_t shiftby) return PyLong_FromLong(0); } #if PY_SSIZE_T_MAX <= INT64_MAX / PyLong_SHIFT - if (shiftby > (int64_t)PY_SSIZE_T_MAX * PyLong_SHIFT) { + /* Keep wordshift <= MAX_LONG_DIGITS, so that oldsize + wordshift + cannot overflow Py_ssize_t in long_lshift1(). */ + if (shiftby > (int64_t)MAX_LONG_DIGITS * PyLong_SHIFT) { PyErr_SetString(PyExc_OverflowError, "too many digits in integer"); return NULL;