gh-146011: Fix use-after-free in `signaldict_repr` after deletion (GH-153784)
(cherry picked from commit
41a087acc2d04c5bc3db93b5367456f05ae400a4)
Co-authored-by: Brij Kapadia <97006829+brijkapadia@users.noreply.github.com>
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
@requires_cdecimal
class CContextFlags(ContextFlags, unittest.TestCase):
decimal = C
+
+ def test_signaldict_repr(self):
+ Context = self.decimal.Context
+ ctx = Context(prec=7)
+ mapping = ctx.flags
+ del ctx
+ with self.assertRaisesRegex(ValueError, 'invalid signal dict'):
+ repr(mapping)
+
class PyContextFlags(ContextFlags, unittest.TestCase):
decimal = P
--- /dev/null
+Fix a heap-use-after-free in the C implementation of :mod:`decimal`
+when calling :func:`repr` after deleting the :class:`~decimal.Context`.
context_clear(PyObject *op)
{
PyDecContextObject *self = _PyDecContextObject_CAST(op);
+ /* Since traps and flags hold a borrowed reference to the
+ flags stored in the context object, these references need
+ to be cleared when the context object is deallocated
+ because traps and flags can survive. See gh-146011. */
+ PyDecSignalDictObject *traps = _PyDecSignalDictObject_CAST(self->traps);
+ PyDecSignalDictObject *flags = _PyDecSignalDictObject_CAST(self->flags);
+
+ if (traps != NULL) {
+ traps->flags = NULL;
+ }
+ if (flags != NULL) {
+ flags->flags = NULL;
+ }
+
Py_CLEAR(self->traps);
Py_CLEAR(self->flags);
return 0;