From fb94e45babe42f4bc05850b303e57c8dead248fb Mon Sep 17 00:00:00 2001 From: CharlieZhao Date: Fri, 30 Jun 2023 16:27:56 +0800 Subject: [PATCH 1/7] Fix segfault in signaldict --- Lib/test/test_decimal.py | 11 +++++++++++ Modules/_decimal/_decimal.c | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index db67f37608f1f2..a77348e278eedf 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -5701,6 +5701,17 @@ def test_c_disallow_instantiation(self): ContextManager = type(C.localcontext()) check_disallow_instantiation(self, ContextManager) + def test_c_signaldict_repr_segfault(self): + # See issue 106263 for details. + SignalDict = type(C.Context().flags) + s = repr(SignalDict()) # This should not segfault + t = "{:False, :False, " \ + ":False, :False, " \ + ":False, :False, " \ + ":False, :False, " \ + ":False}" + self.assertEqual(s, t) + @requires_docstrings @requires_cdecimal class SignatureTest(unittest.TestCase): diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index b7cb19515b3002..de6fca87dc7dbd 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -548,10 +548,12 @@ getround(PyObject *v) initialized to new SignalDicts. Once a SignalDict is tied to a context, it cannot be deleted. */ +static mpd_context_t dflt_ctx; + static int signaldict_init(PyObject *self, PyObject *args UNUSED, PyObject *kwds UNUSED) { - SdFlagAddr(self) = NULL; + SdFlagAddr(self) = &dflt_ctx.status; return 0; } From b035a5e956de2137325a9e47edf78727d9cad53d Mon Sep 17 00:00:00 2001 From: CharlieZhao Date: Fri, 30 Jun 2023 16:50:24 +0800 Subject: [PATCH 2/7] Add NEWS --- .../next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst diff --git a/Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst b/Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst new file mode 100644 index 00000000000000..23763818d84ba5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-06-30-16-42-44.gh-issue-106263.tk-t93.rst @@ -0,0 +1,2 @@ +Fix crash when calling ``repr`` with a manually constructed SignalDict object. +Patch by Charlie Zhao. \ No newline at end of file From 7ec5f154339ebce5d1d87590e5d42a67d107fe28 Mon Sep 17 00:00:00 2001 From: Charlie Zhao Date: Fri, 30 Jun 2023 18:02:11 +0800 Subject: [PATCH 3/7] Update Lib/test/test_decimal.py Co-authored-by: sunmy2019 <59365878+sunmy2019@users.noreply.github.com> --- 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 a77348e278eedf..c0ffc4a82def71 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -5702,7 +5702,7 @@ def test_c_disallow_instantiation(self): check_disallow_instantiation(self, ContextManager) def test_c_signaldict_repr_segfault(self): - # See issue 106263 for details. + # See gh-106263 for details. SignalDict = type(C.Context().flags) s = repr(SignalDict()) # This should not segfault t = "{:False, :False, " \ From 773167e8d2f4fedfaaf9570019d7829ae3e37faf Mon Sep 17 00:00:00 2001 From: CharlieZhao Date: Sat, 1 Jul 2023 21:55:56 +0800 Subject: [PATCH 4/7] Add pointer checker and test cases --- Lib/test/test_decimal.py | 35 ++++++++++++++++++++------- Modules/_decimal/_decimal.c | 47 +++++++++++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index c0ffc4a82def71..fc66a309788ac1 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -5701,16 +5701,35 @@ def test_c_disallow_instantiation(self): ContextManager = type(C.localcontext()) check_disallow_instantiation(self, ContextManager) - def test_c_signaldict_repr_segfault(self): + def test_c_signaldict_segfault(self): # See gh-106263 for details. SignalDict = type(C.Context().flags) - s = repr(SignalDict()) # This should not segfault - t = "{:False, :False, " \ - ":False, :False, " \ - ":False, :False, " \ - ":False, :False, " \ - ":False}" - self.assertEqual(s, t) + sd = SignalDict() + err_msg = "invalid signal dict" + + with self.assertRaisesRegex(ValueError, err_msg): + len(sd) + + with self.assertRaisesRegex(ValueError, err_msg): + iter(sd) + + with self.assertRaisesRegex(ValueError, err_msg): + repr(sd) + + with self.assertRaisesRegex(ValueError, err_msg): + sd[C.InvalidOperation] = True + + with self.assertRaisesRegex(ValueError, err_msg): + sd[C.InvalidOperation] + + with self.assertRaisesRegex(ValueError, err_msg): + sd == C.Context().flags + + with self.assertRaisesRegex(ValueError, err_msg): + C.Context().flags == sd + + with self.assertRaisesRegex(ValueError, err_msg): + sd.copy() @requires_docstrings @requires_cdecimal diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index de6fca87dc7dbd..340aa6f4f4e8fd 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -548,25 +548,33 @@ getround(PyObject *v) initialized to new SignalDicts. Once a SignalDict is tied to a context, it cannot be deleted. */ -static mpd_context_t dflt_ctx; - static int signaldict_init(PyObject *self, PyObject *args UNUSED, PyObject *kwds UNUSED) { - SdFlagAddr(self) = &dflt_ctx.status; + SdFlagAddr(self) = NULL; return 0; } static Py_ssize_t -signaldict_len(PyObject *self UNUSED) +signaldict_len(PyObject *self) { + if (SdFlagAddr(self) == NULL) { + PyErr_SetString(PyExc_ValueError, + "invalid signal dict"); + return -1; + } return SIGNAL_MAP_LEN; } static PyObject *SignalTuple; static PyObject * -signaldict_iter(PyObject *self UNUSED) +signaldict_iter(PyObject *self) { + if (SdFlagAddr(self) == NULL) { + PyErr_SetString(PyExc_ValueError, + "invalid signal dict"); + return NULL; + } return PyTuple_Type.tp_iter(SignalTuple); } @@ -575,6 +583,12 @@ signaldict_getitem(PyObject *self, PyObject *key) { uint32_t flag; + if (SdFlagAddr(self) == NULL) { + PyErr_SetString(PyExc_ValueError, + "invalid signal dict"); + return NULL; + } + flag = exception_as_flag(key); if (flag & DEC_ERRORS) { return NULL; @@ -589,6 +603,12 @@ signaldict_setitem(PyObject *self, PyObject *key, PyObject *value) uint32_t flag; int x; + if (SdFlagAddr(self) == NULL) { + PyErr_SetString(PyExc_ValueError, + "invalid signal dict"); + return -1; + } + if (value == NULL) { return value_error_int("signal keys cannot be deleted"); } @@ -637,6 +657,12 @@ signaldict_repr(PyObject *self) const char *b[SIGNAL_MAP_LEN]; /* bool */ int i; + if (SdFlagAddr(self) == NULL) { + PyErr_SetString(PyExc_ValueError, + "invalid signal dict"); + return NULL; + } + assert(SIGNAL_MAP_LEN == 9); for (cm=signal_map, i=0; cm->name != NULL; cm++, i++) { @@ -660,6 +686,12 @@ signaldict_richcompare(PyObject *v, PyObject *w, int op) decimal_state *state = GLOBAL_STATE(); assert(PyDecSignalDict_Check(state, v)); + if ((SdFlagAddr(v) == NULL) || (SdFlagAddr(w) == NULL)) { + PyErr_SetString(PyExc_ValueError, + "invalid signal dict"); + return NULL; + } + if (op == Py_EQ || op == Py_NE) { if (PyDecSignalDict_Check(state, w)) { res = (SdFlags(v)==SdFlags(w)) ^ (op==Py_NE) ? Py_True : Py_False; @@ -687,6 +719,11 @@ signaldict_richcompare(PyObject *v, PyObject *w, int op) static PyObject * signaldict_copy(PyObject *self, PyObject *args UNUSED) { + if (SdFlagAddr(self) == NULL) { + PyErr_SetString(PyExc_ValueError, + "invalid signal dict"); + return NULL; + } return flags_as_dict(SdFlags(self)); } From 1880fc1329a6164d8ecb70d7080c29ea470b289f Mon Sep 17 00:00:00 2001 From: CharlieZhao Date: Wed, 5 Jul 2023 19:56:21 +0800 Subject: [PATCH 5/7] use `value_error_int` and `value_error_ptr` --- Modules/_decimal/_decimal.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 340aa6f4f4e8fd..c2f639dbd3f8d2 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -254,14 +254,12 @@ value_error_int(const char *mesg) return -1; } -#ifdef CONFIG_32 static PyObject * value_error_ptr(const char *mesg) { PyErr_SetString(PyExc_ValueError, mesg); return NULL; } -#endif static int type_error_int(const char *mesg) @@ -548,6 +546,8 @@ getround(PyObject *v) initialized to new SignalDicts. Once a SignalDict is tied to a context, it cannot be deleted. */ +#define INVALID_SIGNALDICT_ERROR_MSG "invalid signal dict" + static int signaldict_init(PyObject *self, PyObject *args UNUSED, PyObject *kwds UNUSED) { @@ -559,9 +559,7 @@ static Py_ssize_t signaldict_len(PyObject *self) { if (SdFlagAddr(self) == NULL) { - PyErr_SetString(PyExc_ValueError, - "invalid signal dict"); - return -1; + return value_error_int(INVALID_SIGNALDICT_ERROR_MSG); } return SIGNAL_MAP_LEN; } @@ -571,9 +569,7 @@ static PyObject * signaldict_iter(PyObject *self) { if (SdFlagAddr(self) == NULL) { - PyErr_SetString(PyExc_ValueError, - "invalid signal dict"); - return NULL; + return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG); } return PyTuple_Type.tp_iter(SignalTuple); } @@ -584,9 +580,7 @@ signaldict_getitem(PyObject *self, PyObject *key) uint32_t flag; if (SdFlagAddr(self) == NULL) { - PyErr_SetString(PyExc_ValueError, - "invalid signal dict"); - return NULL; + return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG); } flag = exception_as_flag(key); @@ -604,9 +598,7 @@ signaldict_setitem(PyObject *self, PyObject *key, PyObject *value) int x; if (SdFlagAddr(self) == NULL) { - PyErr_SetString(PyExc_ValueError, - "invalid signal dict"); - return -1; + return value_error_int(INVALID_SIGNALDICT_ERROR_MSG); } if (value == NULL) { @@ -658,9 +650,7 @@ signaldict_repr(PyObject *self) int i; if (SdFlagAddr(self) == NULL) { - PyErr_SetString(PyExc_ValueError, - "invalid signal dict"); - return NULL; + return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG); } assert(SIGNAL_MAP_LEN == 9); @@ -687,9 +677,7 @@ signaldict_richcompare(PyObject *v, PyObject *w, int op) assert(PyDecSignalDict_Check(state, v)); if ((SdFlagAddr(v) == NULL) || (SdFlagAddr(w) == NULL)) { - PyErr_SetString(PyExc_ValueError, - "invalid signal dict"); - return NULL; + return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG); } if (op == Py_EQ || op == Py_NE) { @@ -720,9 +708,7 @@ static PyObject * signaldict_copy(PyObject *self, PyObject *args UNUSED) { if (SdFlagAddr(self) == NULL) { - PyErr_SetString(PyExc_ValueError, - "invalid signal dict"); - return NULL; + return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG); } return flags_as_dict(SdFlags(self)); } From d3e24fdb1049ff90a5c38cf8112a64ddedd7a39a Mon Sep 17 00:00:00 2001 From: CharlieZhao Date: Fri, 7 Jul 2023 17:10:36 +0800 Subject: [PATCH 6/7] Replace macro with const statement --- Modules/_decimal/_decimal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index c2f639dbd3f8d2..4e659854c6bbee 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -546,7 +546,7 @@ getround(PyObject *v) initialized to new SignalDicts. Once a SignalDict is tied to a context, it cannot be deleted. */ -#define INVALID_SIGNALDICT_ERROR_MSG "invalid signal dict" +static const char *INVALID_SIGNALDICT_ERROR_MSG = "invalid signal dict"; static int signaldict_init(PyObject *self, PyObject *args UNUSED, PyObject *kwds UNUSED) From 68e03f2c39baafcdf090080cfe596385b6160cd0 Mon Sep 17 00:00:00 2001 From: CharlieZhao Date: Fri, 28 Jul 2023 15:42:26 +0800 Subject: [PATCH 7/7] Fix global variables check --- Tools/c-analyzer/cpython/ignored.tsv | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index cf6d3b24435238..099f20b5a1b433 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -213,6 +213,7 @@ Modules/_decimal/_decimal.c - invalid_rounding_err - Modules/_decimal/_decimal.c - invalid_signals_err - Modules/_decimal/_decimal.c - signal_map_template - Modules/_decimal/_decimal.c - ssize_constants - +Modules/_decimal/_decimal.c - INVALID_SIGNALDICT_ERROR_MSG - Modules/_elementtree.c - ExpatMemoryHandler - Modules/_hashopenssl.c - py_hashes - Modules/_hacl/Hacl_Hash_SHA1.c - _h0 -