gh-135075: Make PyObject_SetAttr() fail with NULL value and exception… · python/cpython@da79ac9 · GitHub | Latest TMZ Celebrity News & Gossip | Watch TMZ Live
Skip to content

Commit da79ac9

Browse files
authored
gh-135075: Make PyObject_SetAttr() fail with NULL value and exception (#136180)
Make PyObject_SetAttr() and PyObject_SetAttrString() fail if called with NULL value and an exception set.
1 parent b2e498a commit da79ac9

File tree

5 files changed

+109
-9
lines changed

5 files changed

+109
-9
lines changed

Doc/c-api/object.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ Object Protocol
197197
in favour of using :c:func:`PyObject_DelAttr`, but there are currently no
198198
plans to remove it.
199199
200+
The function must not be called with ``NULL`` *v* and an an exception set.
201+
This case can arise from forgetting ``NULL`` checks and would delete the
202+
attribute.
203+
204+
.. versionchanged:: next
205+
Must not be called with NULL value if an exception is set.
206+
200207
201208
.. c:function:: int PyObject_SetAttrString(PyObject *o, const char *attr_name, PyObject *v)
202209
@@ -207,6 +214,10 @@ Object Protocol
207214
If *v* is ``NULL``, the attribute is deleted, but this feature is
208215
deprecated in favour of using :c:func:`PyObject_DelAttrString`.
209216
217+
The function must not be called with ``NULL`` *v* and an an exception set.
218+
This case can arise from forgetting ``NULL`` checks and would delete the
219+
attribute.
220+
210221
The number of different attribute names passed to this function
211222
should be kept small, usually by using a statically allocated string
212223
as *attr_name*.
@@ -215,6 +226,10 @@ Object Protocol
215226
For more details, see :c:func:`PyUnicode_InternFromString`, which may be
216227
used internally to create a key object.
217228
229+
.. versionchanged:: next
230+
Must not be called with NULL value if an exception is set.
231+
232+
218233
.. c:function:: int PyObject_GenericSetAttr(PyObject *o, PyObject *name, PyObject *value)
219234
220235
Generic attribute setter and deleter function that is meant

Lib/test/test_capi/test_abstract.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,31 @@ def test_iter_nextitem(self):
10771077
with self.assertRaisesRegex(TypeError, regex):
10781078
PyIter_NextItem(10)
10791079

1080+
def test_object_setattr_null_exc(self):
1081+
class Obj:
1082+
pass
1083+
obj = Obj()
1084+
obj.attr = 123
1085+
1086+
exc = ValueError("error")
1087+
with self.assertRaises(SystemError) as cm:
1088+
_testcapi.object_setattr_null_exc(obj, 'attr', exc)
1089+
self.assertIs(cm.exception.__context__, exc)
1090+
self.assertIsNone(cm.exception.__cause__)
1091+
self.assertHasAttr(obj, 'attr')
1092+
1093+
with self.assertRaises(SystemError) as cm:
1094+
_testcapi.object_setattrstring_null_exc(obj, 'attr', exc)
1095+
self.assertIs(cm.exception.__context__, exc)
1096+
self.assertIsNone(cm.exception.__cause__)
1097+
self.assertHasAttr(obj, 'attr')
1098+
1099+
with self.assertRaises(SystemError) as cm:
1100+
# undecodable name
1101+
_testcapi.object_setattrstring_null_exc(obj, b'\xff', exc)
1102+
self.assertIs(cm.exception.__context__, exc)
1103+
self.assertIsNone(cm.exception.__cause__)
1104+
10801105

10811106
if __name__ == "__main__":
10821107
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Make :c:func:`PyObject_SetAttr` and :c:func:`PyObject_SetAttrString` fail if
2+
called with ``NULL`` value and an exception set. Patch by Victor Stinner.

Modules/_testcapi/abstract.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,42 @@ sequence_fast_get_item(PyObject *self, PyObject *args)
178178
}
179179

180180

181+
static PyObject *
182+
object_setattr_null_exc(PyObject *self, PyObject *args)
183+
{
184+
PyObject *obj, *name, *exc;
185+
if (!PyArg_ParseTuple(args, "OOO", &obj, &name, &exc)) {
186+
return NULL;
187+
}
188+
189+
PyErr_SetObject((PyObject*)Py_TYPE(exc), exc);
190+
if (PyObject_SetAttr(obj, name, NULL) < 0) {
191+
return NULL;
192+
}
193+
assert(PyErr_Occurred());
194+
return NULL;
195+
}
196+
197+
198+
static PyObject *
199+
object_setattrstring_null_exc(PyObject *self, PyObject *args)
200+
{
201+
PyObject *obj, *exc;
202+
const char *name;
203+
Py_ssize_t size;
204+
if (!PyArg_ParseTuple(args, "Oz#O", &obj, &name, &size, &exc)) {
205+
return NULL;
206+
}
207+
208+
PyErr_SetObject((PyObject*)Py_TYPE(exc), exc);
209+
if (PyObject_SetAttrString(obj, name, NULL) < 0) {
210+
return NULL;
211+
}
212+
assert(PyErr_Occurred());
213+
return NULL;
214+
}
215+
216+
181217
static PyMethodDef test_methods[] = {
182218
{"object_getoptionalattr", object_getoptionalattr, METH_VARARGS},
183219
{"object_getoptionalattrstring", object_getoptionalattrstring, METH_VARARGS},
@@ -191,6 +227,8 @@ static PyMethodDef test_methods[] = {
191227

192228
{"sequence_fast_get_size", sequence_fast_get_size, METH_O},
193229
{"sequence_fast_get_item", sequence_fast_get_item, METH_VARARGS},
230+
{"object_setattr_null_exc", object_setattr_null_exc, METH_VARARGS},
231+
{"object_setattrstring_null_exc", object_setattrstring_null_exc, METH_VARARGS},
194232
{NULL},
195233
};
196234

Objects/object.c

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,16 +1213,27 @@ PyObject_HasAttrString(PyObject *obj, const char *name)
12131213
int
12141214
PyObject_SetAttrString(PyObject *v, const char *name, PyObject *w)
12151215
{
1216-
PyObject *s;
1217-
int res;
1216+
PyThreadState *tstate = _PyThreadState_GET();
1217+
if (w == NULL && _PyErr_Occurred(tstate)) {
1218+
PyObject *exc = _PyErr_GetRaisedException(tstate);
1219+
_PyErr_SetString(tstate, PyExc_SystemError,
1220+
"PyObject_SetAttrString() must not be called with NULL value "
1221+
"and an exception set");
1222+
_PyErr_ChainExceptions1Tstate(tstate, exc);
1223+
return -1;
1224+
}
12181225

1219-
if (Py_TYPE(v)->tp_setattr != NULL)
1226+
if (Py_TYPE(v)->tp_setattr != NULL) {
12201227
return (*Py_TYPE(v)->tp_setattr)(v, (char*)name, w);
1221-
s = PyUnicode_InternFromString(name);
1222-
if (s == NULL)
1228+
}
1229+
1230+
PyObject *s = PyUnicode_InternFromString(name);
1231+
if (s == NULL) {
12231232
return -1;
1224-
res = PyObject_SetAttr(v, s, w);
1225-
Py_XDECREF(s);
1233+
}
1234+
1235+
int res = PyObject_SetAttr(v, s, w);
1236+
Py_DECREF(s);
12261237
return res;
12271238
}
12281239

@@ -1440,6 +1451,16 @@ PyObject_HasAttr(PyObject *obj, PyObject *name)
14401451
int
14411452
PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
14421453
{
1454+
PyThreadState *tstate = _PyThreadState_GET();
1455+
if (value == NULL && _PyErr_Occurred(tstate)) {
1456+
PyObject *exc = _PyErr_GetRaisedException(tstate);
1457+
_PyErr_SetString(tstate, PyExc_SystemError,
1458+
"PyObject_SetAttr() must not be called with NULL value "
1459+
"and an exception set");
1460+
_PyErr_ChainExceptions1Tstate(tstate, exc);
1461+
return -1;
1462+
}
1463+
14431464
PyTypeObject *tp = Py_TYPE(v);
14441465
int err;
14451466

@@ -1451,8 +1472,7 @@ PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
14511472
}
14521473
Py_INCREF(name);
14531474

1454-
PyInterpreterState *interp = _PyInterpreterState_GET();
1455-
_PyUnicode_InternMortal(interp, &name);
1475+
_PyUnicode_InternMortal(tstate->interp, &name);
14561476
if (tp->tp_setattro != NULL) {
14571477
err = (*tp->tp_setattro)(v, name, value);
14581478
Py_DECREF(name);

0 commit comments

Comments
 (0)

TMZ Celebrity News – Breaking Stories, Videos & Gossip

Looking for the latest TMZ celebrity news? You've come to the right place. From shocking Hollywood scandals to exclusive videos, TMZ delivers it all in real time.

Whether it’s a red carpet slip-up, a viral paparazzi moment, or a legal drama involving your favorite stars, TMZ news is always first to break the story. Stay in the loop with daily updates, insider tips, and jaw-dropping photos.

🎥 Watch TMZ Live

TMZ Live brings you daily celebrity news and interviews straight from the TMZ newsroom. Don’t miss a beat—watch now and see what’s trending in Hollywood.