self.assertEqual(pylongwriter_create(negative, digits), num,
(negative, digits))
+ @unittest.skipUnless(support.Py_DEBUG, "need a debug build (Py_DEBUG)")
+ def test_longwriter_finish(self):
+ # Test PyLongWriter_Create(0, 3, &digits) with PyLongWriter_Finish()
+ # where the last digit is left uninitialized
+ pylongwriter_finish_bug = _testcapi.pylongwriter_finish_bug
+ with self.assertRaises(SystemError) as cm:
+ pylongwriter_finish_bug()
+ self.assertEqual(str(cm.exception),
+ 'PyLongWriter_Finish: digit 2 is uninitialized')
+
def test_bug_143050(self):
with support.adjust_int_max_str_digits(0):
# Bug coming from using _pylong.int_from_string(), that
}
+static PyObject *
+pylongwriter_finish_bug(PyObject *module, PyObject *Py_UNUSED(args))
+{
+ void *writer_digits;
+ PyLongWriter *writer = PyLongWriter_Create(0, 3, &writer_digits);
+ if (writer == NULL) {
+ return NULL;
+ }
+
+ assert(PyLong_GetNativeLayout()->digit_size == sizeof(digit));
+ digit *digits = writer_digits;
+ digits[0] = 1;
+ digits[1] = 1;
+ // Oops, digits[2] is left uninitialized on purpose
+ // to test PyLongWriter_Finish()
+ return PyLongWriter_Finish(writer);
+}
+
+
static PyObject *
get_pylong_layout(PyObject *module, PyObject *Py_UNUSED(args))
{
{"pylong_aspid", pylong_aspid, METH_O},
{"pylong_export", pylong_export, METH_O},
{"pylongwriter_create", pylongwriter_create, METH_VARARGS},
+ {"pylongwriter_finish_bug", pylongwriter_finish_bug, METH_NOARGS},
{"get_pylong_layout", get_pylong_layout, METH_NOARGS},
{"pylong_ispositive", pylong_ispositive, METH_O},
{"pylong_isnegative", pylong_isnegative, METH_O},
_PyLong_InitTag(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;
+#ifdef Py_DEBUG
+ // gh-147988: Fill digits with an invalid pattern to catch usage
+ // of uninitialized digits.
+ memset(result->long_value.ob_digit, 0xFF, ndigits * sizeof(digit));
+#endif
return result;
}
int sign = is_signed ? -1: 1;
if (idigit == 0) {
sign = 0;
+ v->long_value.ob_digit[0] = 0;
}
_PyLong_SetSignAndDigitCount(v, sign, idigit);
return (PyObject *)maybe_small_long(long_normalize(v));
*res = NULL;
return 0;
}
+ z->long_value.ob_digit[0] = 0;
_PyLong_SetSignAndDigitCount(z, 0, 0);
/* `convwidth` consecutive input digits are treated as a single
*prem = NULL;
return NULL;
}
+ a->long_value.ob_digit[0] = 0;
v0 = v->long_value.ob_digit;
w0 = w->long_value.ob_digit;
wm1 = w0[size_w-1];
/* 1. Allocate result space. */
ret = long_alloc(asize + bsize);
if (ret == NULL) goto fail;
-#ifdef Py_DEBUG
- /* Fill with trash, to catch reference to uninitialized digits. */
- memset(ret->long_value.ob_digit, 0xDF, _PyLong_DigitCount(ret) * sizeof(digit));
-#endif
/* 2. t1 <- ah*bh, and copy into high digits of result. */
if ((t1 = k_mul(ah, bh)) == NULL) goto fail;
Py_UNREACHABLE();
}
+ if ((size_z + negz) == 0) {
+ Py_XDECREF(new_a);
+ Py_XDECREF(new_b);
+ return get_small_int(0);
+ }
+
/* We allow an extra digit if z is negative, to make sure that
the final two's complement of z doesn't overflow. */
z = long_alloc(size_z + negz);
PyLongObject *obj = (PyLongObject *)writer;
assert(Py_REFCNT(obj) == 1);
+#ifdef Py_DEBUG
+ // gh-147988: Detect uninitialized digits: long_alloc() fills digits with
+ // 0xFF byte pattern. It's posssible because PyLong_BASE is smaller than
+ // the maximum value of the C digit type (uint32_t or unsigned short):
+ // most significan bits are unused by the API.
+ Py_ssize_t ndigits = _PyLong_DigitCount(obj);
+ if (ndigits == 0) {
+ // Check ob_digit[0] digit for the number zero
+ ndigits = 1;
+ }
+ for (Py_ssize_t i = 0; i < ndigits; i++) {
+ digit d = obj->long_value.ob_digit[i];
+ if (d & ~(digit)PyLong_MASK) {
+ Py_DECREF(obj);
+ PyErr_Format(PyExc_SystemError,
+ "PyLongWriter_Finish: digit %zd is uninitialized",
+ i);
+ return NULL;
+ }
+ }
+#endif
+
// Normalize and get singleton if possible
obj = maybe_small_long(long_normalize(obj));