From d14c22982753a7d48d5b38308bf135f7a943f0de Mon Sep 17 00:00:00 2001 From: Brij <97006829+brijkapadia@users.noreply.github.com> Date: Wed, 15 Jul 2026 17:48:48 -0400 Subject: [PATCH 1/6] gh-146011: Fix use-after-free in `signaldict_repr` after deletion --- Lib/test/test_decimal.py | 9 +++++++++ Modules/_decimal/_decimal.c | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index a04ed0c83c07c4a..e261fee4d875d7d 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -4147,6 +4147,15 @@ def test_float_operation_default(self): @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.assertRaises(ValueError): + repr(mapping) + class PyContextFlags(ContextFlags, unittest.TestCase): decimal = P diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index dc1b3c06bed9521..2fce263cf107e3c 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -1499,6 +1499,10 @@ static int context_clear(PyObject *op) { PyDecContextObject *self = _PyDecContextObject_CAST(op); + PyDecSignalDictObject *traps = _PyDecSignalDictObject_CAST(self->traps); + PyDecSignalDictObject *flags = _PyDecSignalDictObject_CAST(self->flags); + traps->flags = NULL; + flags->flags = NULL; Py_CLEAR(self->traps); Py_CLEAR(self->flags); return 0; From 423628a03a6e86a27cdfbc5d415e4ed9aa190b17 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 15 Jul 2026 21:56:42 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2026-07-15-21-56-40.gh-issue-146011.nWmHif.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-07-15-21-56-40.gh-issue-146011.nWmHif.rst diff --git a/Misc/NEWS.d/next/Library/2026-07-15-21-56-40.gh-issue-146011.nWmHif.rst b/Misc/NEWS.d/next/Library/2026-07-15-21-56-40.gh-issue-146011.nWmHif.rst new file mode 100644 index 000000000000000..88f4e39011baa75 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-15-21-56-40.gh-issue-146011.nWmHif.rst @@ -0,0 +1,2 @@ +Fix a heap-use-after-free in the C implementation of :mod:`decimal` +when calling :func:`repr` after deleting the :class:`~decimal.Context`. From 35185dcb507726514fbf77871946e741043094e4 Mon Sep 17 00:00:00 2001 From: Brij <97006829+brijkapadia@users.noreply.github.com> Date: Wed, 15 Jul 2026 18:08:37 -0400 Subject: [PATCH 3/6] fix segv --- .../2026-07-15-21-56-40.gh-issue-146011.nWmHif.rst | 2 +- Modules/_decimal/_decimal.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2026-07-15-21-56-40.gh-issue-146011.nWmHif.rst b/Misc/NEWS.d/next/Library/2026-07-15-21-56-40.gh-issue-146011.nWmHif.rst index 88f4e39011baa75..0cac0257040bd6b 100644 --- a/Misc/NEWS.d/next/Library/2026-07-15-21-56-40.gh-issue-146011.nWmHif.rst +++ b/Misc/NEWS.d/next/Library/2026-07-15-21-56-40.gh-issue-146011.nWmHif.rst @@ -1,2 +1,2 @@ -Fix a heap-use-after-free in the C implementation of :mod:`decimal` +Fix a heap-use-after-free in the C implementation of :mod:`decimal` when calling :func:`repr` after deleting the :class:`~decimal.Context`. diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 2fce263cf107e3c..05d00acfe89bc72 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -1501,8 +1501,14 @@ context_clear(PyObject *op) PyDecContextObject *self = _PyDecContextObject_CAST(op); PyDecSignalDictObject *traps = _PyDecSignalDictObject_CAST(self->traps); PyDecSignalDictObject *flags = _PyDecSignalDictObject_CAST(self->flags); - traps->flags = NULL; - flags->flags = NULL; + + if (traps != NULL) { + traps->flags = NULL; + } + if (flags != NULL) { + flags->flags = NULL; + } + Py_CLEAR(self->traps); Py_CLEAR(self->flags); return 0; From dcd7fa83618d30129cdae78c2d70b804cbfc61e4 Mon Sep 17 00:00:00 2001 From: Brij Kapadia <97006829+brijkapadia@users.noreply.github.com> Date: Fri, 24 Jul 2026 07:11:21 -0400 Subject: [PATCH 4/6] Check error message Co-authored-by: Victor Stinner --- Lib/test/test_decimal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index e261fee4d875d7d..1c723b25784da11 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -4153,7 +4153,7 @@ def test_signaldict_repr(self): ctx = Context(prec=7) mapping = ctx.flags del ctx - with self.assertRaises(ValueError): + with self.assertRaisesRegex(ValueError, 'invalid signal dict'): repr(mapping) class PyContextFlags(ContextFlags, unittest.TestCase): From d43f8a5a0a7c878ca6801ccf6f78e56d97af2e03 Mon Sep 17 00:00:00 2001 From: Brij <97006829+brijkapadia@users.noreply.github.com> Date: Fri, 24 Jul 2026 07:23:47 -0400 Subject: [PATCH 5/6] add comment --- Modules/_decimal/_decimal.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 05d00acfe89bc72..1bc0cace22d8b77 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -1499,6 +1499,11 @@ static int 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 other objects could hold references to the + traps and/or flags objects. See gh-146011. */ PyDecSignalDictObject *traps = _PyDecSignalDictObject_CAST(self->traps); PyDecSignalDictObject *flags = _PyDecSignalDictObject_CAST(self->flags); From 076eb2b53411c7770b4d7cf5c04cfa07cdc6287f Mon Sep 17 00:00:00 2001 From: Brij Kapadia <97006829+brijkapadia@users.noreply.github.com> Date: Fri, 24 Jul 2026 07:49:22 -0400 Subject: [PATCH 6/6] Improve comment Co-authored-by: Victor Stinner --- Modules/_decimal/_decimal.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 1bc0cace22d8b77..6fbce0ed85e695d 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -1502,8 +1502,7 @@ context_clear(PyObject *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 other objects could hold references to the - traps and/or flags objects. See gh-146011. */ + because traps and flags can survive. See gh-146011. */ PyDecSignalDictObject *traps = _PyDecSignalDictObject_CAST(self->traps); PyDecSignalDictObject *flags = _PyDecSignalDictObject_CAST(self->flags);