Skip to content

[3.11] gh-106263: Fix segfault in signaldict_repr in _decimal module (#… #107490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Lib/test/test_decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -5682,6 +5682,36 @@ def test_maxcontext_exact_arith(self):
self.assertEqual(Decimal(400) ** -1, Decimal('0.0025'))


def test_c_signaldict_segfault(self):
# See gh-106263 for details.
SignalDict = type(C.Context().flags)
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
class SignatureTest(unittest.TestCase):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix crash when calling ``repr`` with a manually constructed SignalDict object.
Patch by Charlie Zhao.
30 changes: 27 additions & 3 deletions Modules/_decimal/_decimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,14 +247,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)
Expand Down Expand Up @@ -541,6 +539,8 @@ getround(PyObject *v)
initialized to new SignalDicts. Once a SignalDict is tied to
a context, it cannot be deleted. */

static const char *INVALID_SIGNALDICT_ERROR_MSG = "invalid signal dict";

static int
signaldict_init(PyObject *self, PyObject *args UNUSED, PyObject *kwds UNUSED)
{
Expand All @@ -549,22 +549,31 @@ signaldict_init(PyObject *self, PyObject *args UNUSED, PyObject *kwds UNUSED)
}

static Py_ssize_t
signaldict_len(PyObject *self UNUSED)
signaldict_len(PyObject *self)
{
if (SdFlagAddr(self) == NULL) {
return value_error_int(INVALID_SIGNALDICT_ERROR_MSG);
}
return SIGNAL_MAP_LEN;
}

static PyObject *SignalTuple;
static PyObject *
signaldict_iter(PyObject *self UNUSED)
{
if (SdFlagAddr(self) == NULL) {
return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG);
}
return PyTuple_Type.tp_iter(SignalTuple);
}

static PyObject *
signaldict_getitem(PyObject *self, PyObject *key)
{
uint32_t flag;
if (SdFlagAddr(self) == NULL) {
return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG);
}

flag = exception_as_flag(key);
if (flag & DEC_ERRORS) {
Expand All @@ -580,6 +589,10 @@ signaldict_setitem(PyObject *self, PyObject *key, PyObject *value)
uint32_t flag;
int x;

if (SdFlagAddr(self) == NULL) {
return value_error_int(INVALID_SIGNALDICT_ERROR_MSG);
}

if (value == NULL) {
return value_error_int("signal keys cannot be deleted");
}
Expand Down Expand Up @@ -612,6 +625,10 @@ signaldict_repr(PyObject *self)
const char *b[SIGNAL_MAP_LEN]; /* bool */
int i;

if (SdFlagAddr(self) == NULL) {
return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG);
}

assert(SIGNAL_MAP_LEN == 9);

for (cm=signal_map, i=0; cm->name != NULL; cm++, i++) {
Expand All @@ -634,6 +651,10 @@ signaldict_richcompare(PyObject *v, PyObject *w, int op)

assert(PyDecSignalDict_Check(v));

if ((SdFlagAddr(v) == NULL) || (SdFlagAddr(w) == NULL)) {
return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG);
}

if (op == Py_EQ || op == Py_NE) {
if (PyDecSignalDict_Check(w)) {
res = (SdFlags(v)==SdFlags(w)) ^ (op==Py_NE) ? Py_True : Py_False;
Expand Down Expand Up @@ -662,6 +683,9 @@ signaldict_richcompare(PyObject *v, PyObject *w, int op)
static PyObject *
signaldict_copy(PyObject *self, PyObject *args UNUSED)
{
if (SdFlagAddr(self) == NULL) {
return value_error_ptr(INVALID_SIGNALDICT_ERROR_MSG);
}
return flags_as_dict(SdFlags(self));
}

Expand Down
1 change: 1 addition & 0 deletions Tools/c-analyzer/cpython/ignored.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -1432,6 +1432,7 @@ Modules/_decimal/_decimal.c - invalid_rounding_err -
Modules/_decimal/_decimal.c - invalid_signals_err -
Modules/_decimal/_decimal.c - signal_map -
Modules/_decimal/_decimal.c - ssize_constants -
Modules/_decimal/_decimal.c - INVALID_SIGNALDICT_ERROR_MSG -
Modules/_elementtree.c - ExpatMemoryHandler -
Modules/_io/_iomodule.c - static_types -
Modules/_io/textio.c - encodefuncs -
Expand Down