Check dict item accesses where it isn't already checked (#2863)

* Convert PyDict_GetXXX to internal error checking variant

* Check unlikely error return from PyDict_DelItemString
This commit is contained in:
Dustin Spicuzza 2021-07-02 10:02:33 -04:00 committed by GitHub
parent 5bcaaa0423
commit 6d4409466b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 43 additions and 4 deletions

View File

@ -636,7 +636,7 @@ protected:
bool bad_arg = false; bool bad_arg = false;
for (; args_copied < args_to_copy; ++args_copied) { for (; args_copied < args_to_copy; ++args_copied) {
const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr; const argument_record *arg_rec = args_copied < func.args.size() ? &func.args[args_copied] : nullptr;
if (kwargs_in && arg_rec && arg_rec->name && PyDict_GetItemString(kwargs_in, arg_rec->name)) { if (kwargs_in && arg_rec && arg_rec->name && dict_getitemstring(kwargs_in, arg_rec->name)) {
bad_arg = true; bad_arg = true;
break; break;
} }
@ -684,7 +684,7 @@ protected:
handle value; handle value;
if (kwargs_in && arg_rec.name) if (kwargs_in && arg_rec.name)
value = PyDict_GetItemString(kwargs.ptr(), arg_rec.name); value = dict_getitemstring(kwargs.ptr(), arg_rec.name);
if (value) { if (value) {
// Consume a kwargs value // Consume a kwargs value
@ -692,7 +692,9 @@ protected:
kwargs = reinterpret_steal<dict>(PyDict_Copy(kwargs.ptr())); kwargs = reinterpret_steal<dict>(PyDict_Copy(kwargs.ptr()));
copied_kwargs = true; copied_kwargs = true;
} }
PyDict_DelItemString(kwargs.ptr(), arg_rec.name); if (PyDict_DelItemString(kwargs.ptr(), arg_rec.name) == -1) {
throw error_already_set();
}
} else if (arg_rec.value) { } else if (arg_rec.value) {
value = arg_rec.value; value = arg_rec.value;
} }
@ -2139,7 +2141,7 @@ inline function get_type_override(const void *this_ptr, const type_info *this_ty
if (frame && (std::string) str(frame->f_code->co_name) == name && if (frame && (std::string) str(frame->f_code->co_name) == name &&
frame->f_code->co_argcount > 0) { frame->f_code->co_argcount > 0) {
PyFrame_FastToLocals(frame); PyFrame_FastToLocals(frame);
PyObject *self_caller = PyDict_GetItem( PyObject *self_caller = dict_getitem(
frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0)); frame->f_locals, PyTuple_GET_ITEM(frame->f_code->co_varnames, 0));
if (self_caller == self.ptr()) if (self_caller == self.ptr())
return function(); return function();

View File

@ -485,6 +485,43 @@ inline handle get_function(handle value) {
return value; return value;
} }
// Reimplementation of python's dict helper functions to ensure that exceptions
// aren't swallowed (see #2862)
// copied from cpython _PyDict_GetItemStringWithError
inline PyObject * dict_getitemstring(PyObject *v, const char *key)
{
#if PY_MAJOR_VERSION >= 3
PyObject *kv, *rv;
kv = PyUnicode_FromString(key);
if (kv == NULL) {
throw error_already_set();
}
rv = PyDict_GetItemWithError(v, kv);
Py_DECREF(kv);
if (rv == NULL && PyErr_Occurred()) {
throw error_already_set();
}
return rv;
#else
return PyDict_GetItemString(v, key);
#endif
}
inline PyObject * dict_getitem(PyObject *v, PyObject *key)
{
#if PY_MAJOR_VERSION >= 3
PyObject *rv = PyDict_GetItemWithError(v, key);
if (rv == NULL && PyErr_Occurred()) {
throw error_already_set();
}
return rv;
#else
return PyDict_GetItem(v, key);
#endif
}
// Helper aliases/functions to support implicit casting of values given to python accessors/methods. // Helper aliases/functions to support implicit casting of values given to python accessors/methods.
// When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes // When given a pyobject, this simply returns the pyobject as-is; for other C++ type, the value goes
// through pybind11::cast(obj) to convert it to an `object`. // through pybind11::cast(obj) to convert it to an `object`.