Skip to content

Commit a5d081d

Browse files
gh-129502: Fix handling errors in ctypes callbacks
Unlikely errors in preparing arguments for ctypes callback are now handled in the same way as errors raised in the callback of in converting the result of the callback -- using sys.unraisablehook() instead of sys.excepthook() and not setting sys.last_exc and other variables.
1 parent a810cb8 commit a5d081d

File tree

2 files changed

+35
-36
lines changed

2 files changed

+35
-36
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Unlikely errors in preparing arguments for :mod:`ctypes` callback are now
2+
handled in the same way as errors raised in the callback of in converting
3+
the result of the callback -- using :func:`sys.unraisablehook` instead of
4+
:func:`sys.excepthook` and not setting :data:`sys.last_exc` and other
5+
variables.

Modules/_ctypes/callbacks.c

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -81,22 +81,6 @@ PyType_Spec cthunk_spec = {
8181

8282
/**************************************************************/
8383

84-
static void
85-
PrintError(const char *msg, ...)
86-
{
87-
char buf[512];
88-
PyObject *f = PySys_GetObject("stderr");
89-
va_list marker;
90-
91-
va_start(marker, msg);
92-
PyOS_vsnprintf(buf, sizeof(buf), msg, marker);
93-
va_end(marker);
94-
if (f != NULL && f != Py_None)
95-
PyFile_WriteString(buf, f);
96-
PyErr_Print();
97-
}
98-
99-
10084
#ifdef MS_WIN32
10185
/*
10286
* We must call AddRef() on non-NULL COM pointers we receive as arguments
@@ -108,26 +92,23 @@ PrintError(const char *msg, ...)
10892
* after checking for PyObject_IsTrue(), but this would probably be somewhat
10993
* slower.
11094
*/
111-
static void
95+
static int
11296
TryAddRef(PyObject *cnv, CDataObject *obj)
11397
{
11498
IUnknown *punk;
11599
PyObject *attrdict = _PyType_GetDict((PyTypeObject *)cnv);
116100
if (!attrdict) {
117-
return;
101+
return 0;
118102
}
119103
int r = PyDict_Contains(attrdict, &_Py_ID(_needs_com_addref_));
120104
if (r <= 0) {
121-
if (r < 0) {
122-
PrintError("getting _needs_com_addref_");
123-
}
124-
return;
105+
return r;
125106
}
126107

127108
punk = *(IUnknown **)obj->b_ptr;
128109
if (punk)
129110
punk->lpVtbl->AddRef(punk);
130-
return;
111+
return 0;
131112
}
132113
#endif
133114

@@ -162,14 +143,13 @@ static void _CallPythonObject(ctypes_state *st,
162143

163144
StgInfo *info;
164145
if (PyStgInfo_FromType(st, cnv, &info) < 0) {
165-
goto Done;
146+
goto Error;
166147
}
167148

168149
if (info && info->getfunc && !_ctypes_simple_instance(st, cnv)) {
169150
PyObject *v = info->getfunc(*pArgs, info->size);
170151
if (!v) {
171-
PrintError("create argument %zd:\n", i);
172-
goto Done;
152+
goto Error;
173153
}
174154
args[i] = v;
175155
/* XXX XXX XX
@@ -182,33 +162,39 @@ static void _CallPythonObject(ctypes_state *st,
182162
/* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */
183163
CDataObject *obj = (CDataObject *)_PyObject_CallNoArgs(cnv);
184164
if (!obj) {
185-
PrintError("create argument %zd:\n", i);
186-
goto Done;
165+
goto Error;
187166
}
188167
if (!CDataObject_Check(st, obj)) {
168+
PyErr_Format(PyExc_TypeError,
169+
"%R returned unexpected result of type %T", cnv, obj);
189170
Py_DECREF(obj);
190-
PrintError("unexpected result of create argument %zd:\n", i);
191-
goto Done;
171+
goto Error;
192172
}
193173
memcpy(obj->b_ptr, *pArgs, info->size);
194174
args[i] = (PyObject *)obj;
195175
#ifdef MS_WIN32
196-
TryAddRef(cnv, obj);
176+
if (TryAddRef(cnv, obj) < 0) {
177+
goto Error;
178+
}
197179
#endif
198180
} else {
199-
PyErr_SetString(PyExc_TypeError,
200-
"cannot build parameter");
201-
PrintError("Parsing argument %zd\n", i);
202-
goto Done;
181+
PyErr_Format(PyExc_TypeError,
182+
"cannot build parameter of type %R", cnv);
183+
goto Error;
203184
}
204185
/* XXX error handling! */
205186
pArgs++;
206187
}
207188

208189
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
209190
error_object = _ctypes_get_errobj(st, &space);
210-
if (error_object == NULL)
191+
if (error_object == NULL) {
192+
PyErr_FormatUnraisable(
193+
"Exception ignored on setting error for "
194+
"ctypes callback function %R",
195+
callable);
211196
goto Done;
197+
}
212198
if (flags & FUNCFLAG_USE_ERRNO) {
213199
int temp = space[0];
214200
space[0] = errno;
@@ -295,6 +281,14 @@ static void _CallPythonObject(ctypes_state *st,
295281
for (j = 0; j < i; j++) {
296282
Py_DECREF(args[j]);
297283
}
284+
return;
285+
286+
Error:
287+
PyErr_FormatUnraisable(
288+
"Exception ignored on creating argument %zd for "
289+
"ctypes callback function %R",
290+
i, callable);
291+
goto Done;
298292
}
299293

300294
static void closure_fcn(ffi_cif *cif,

0 commit comments

Comments
 (0)