Skip to content

Commit 4be289e

Browse files
committed
PyLongWriter_Finish() now raises SystemError
PyLongWriter_Finish() now raises SystemError instead of stopping the process with abort(). Add test on PyLongWriter_Finish() bug.
1 parent bb7e393 commit 4be289e

3 files changed

Lines changed: 40 additions & 3 deletions

File tree

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+
@unittest.skipUnless(support.Py_DEBUG, "need a debug build (Py_DEBUG)")
807+
def test_longwriter_finish(self):
808+
# Test PyLongWriter_Create(0, 3, &digits) with PyLongWriter_Finish()
809+
# where the last digit is left uninitialized
810+
pylongwriter_finish_bug = _testcapi.pylongwriter_finish_bug
811+
with self.assertRaises(SystemError) as cm:
812+
pylongwriter_finish_bug()
813+
self.assertEqual(str(cm.exception),
814+
'PyLongWriter_Finish: digit 2 is uninitialized')
815+
806816
def test_bug_143050(self):
807817
with support.adjust_int_max_str_digits(0):
808818
# Bug coming from using _pylong.int_from_string(), that

Modules/_testcapi/long.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,25 @@ pylongwriter_create(PyObject *module, PyObject *args)
254254
}
255255

256256

257+
static PyObject *
258+
pylongwriter_finish_bug(PyObject *module, PyObject *Py_UNUSED(args))
259+
{
260+
void *writer_digits;
261+
PyLongWriter *writer = PyLongWriter_Create(0, 3, &writer_digits);
262+
if (writer == NULL) {
263+
return NULL;
264+
}
265+
266+
assert(PyLong_GetNativeLayout()->digit_size == sizeof(digit));
267+
digit *digits = writer_digits;
268+
digits[0] = 1;
269+
digits[1] = 1;
270+
// Oops, digits[2] is left uninitialized on purpose
271+
// to test PyLongWriter_Finish()
272+
return PyLongWriter_Finish(writer);
273+
}
274+
275+
257276
static PyObject *
258277
get_pylong_layout(PyObject *module, PyObject *Py_UNUSED(args))
259278
{
@@ -271,6 +290,7 @@ static PyMethodDef test_methods[] = {
271290
{"pylong_aspid", pylong_aspid, METH_O},
272291
{"pylong_export", pylong_export, METH_O},
273292
{"pylongwriter_create", pylongwriter_create, METH_VARARGS},
293+
{"pylongwriter_finish_bug", pylongwriter_finish_bug, METH_NOARGS},
274294
{"get_pylong_layout", get_pylong_layout, METH_NOARGS},
275295
{"pylong_ispositive", pylong_ispositive, METH_O},
276296
{"pylong_isnegative", pylong_isnegative, METH_O},

Objects/longobject.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6959,16 +6959,23 @@ PyLongWriter_Finish(PyLongWriter *writer)
69596959
assert(Py_REFCNT(obj) == 1);
69606960

69616961
#ifdef Py_DEBUG
6962-
// gh-147988: Detect uninitialized digits:
6963-
// long_alloc() fills digits with 0xFF byte pattern.
6962+
// gh-147988: Detect uninitialized digits: long_alloc() fills digits with
6963+
// 0xFF byte pattern. It's posssible because PyLong_BASE is smaller than
6964+
// the maximum value of the C digit type (uint32_t or unsigned short).
69646965
Py_ssize_t ndigits = _PyLong_DigitCount(obj);
69656966
if (ndigits == 0) {
69666967
// Check ob_digit[0] digit for the number zero
69676968
ndigits = 1;
69686969
}
69696970
for (Py_ssize_t i = 0; i < ndigits; i++) {
69706971
digit d = obj->long_value.ob_digit[i];
6971-
assert(d < PyLong_BASE);
6972+
if (d >= PyLong_BASE) {
6973+
Py_DECREF(obj);
6974+
PyErr_Format(PyExc_SystemError,
6975+
"PyLongWriter_Finish: digit %zd is uninitialized",
6976+
i);
6977+
return NULL;
6978+
}
69726979
}
69736980
#endif
69746981

0 commit comments

Comments
 (0)