From 537e6f689a37e7737cc4504bda7d15ceee8adbfc Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 31 Jan 2025 15:30:19 +0200 Subject: [PATCH] gh-129502: Fix handling errors in ctypes callbacks (GH-129504) 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. (cherry picked from commit 9d63ae5fe52d95059ab1bcd4cbb1f9e17033c897) Co-authored-by: Serhiy Storchaka --- ...-01-31-11-14-05.gh-issue-129502.j_ArNo.rst | 5 ++ Modules/_ctypes/callbacks.c | 66 +++++++++---------- 2 files changed, 35 insertions(+), 36 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst diff --git a/Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst b/Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst new file mode 100644 index 00000000000000..e9e9d12c11d0ac --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst @@ -0,0 +1,5 @@ +Unlikely errors in preparing arguments for :mod:`ctypes` callback are now +handled in the same way as errors raised in the callback of in converting +the result of the callback -- using :func:`sys.unraisablehook` instead of +:func:`sys.excepthook` and not setting :data:`sys.last_exc` and other +variables. diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index 7b9f6437c7d55f..675a82577fa9b8 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -81,22 +81,6 @@ PyType_Spec cthunk_spec = { /**************************************************************/ -static void -PrintError(const char *msg, ...) -{ - char buf[512]; - PyObject *f = PySys_GetObject("stderr"); - va_list marker; - - va_start(marker, msg); - PyOS_vsnprintf(buf, sizeof(buf), msg, marker); - va_end(marker); - if (f != NULL && f != Py_None) - PyFile_WriteString(buf, f); - PyErr_Print(); -} - - #ifdef MS_WIN32 /* * We must call AddRef() on non-NULL COM pointers we receive as arguments @@ -108,26 +92,23 @@ PrintError(const char *msg, ...) * after checking for PyObject_IsTrue(), but this would probably be somewhat * slower. */ -static void +static int TryAddRef(PyObject *cnv, CDataObject *obj) { IUnknown *punk; PyObject *attrdict = _PyType_GetDict((PyTypeObject *)cnv); if (!attrdict) { - return; + return 0; } int r = PyDict_Contains(attrdict, &_Py_ID(_needs_com_addref_)); if (r <= 0) { - if (r < 0) { - PrintError("getting _needs_com_addref_"); - } - return; + return r; } punk = *(IUnknown **)obj->b_ptr; if (punk) punk->lpVtbl->AddRef(punk); - return; + return 0; } #endif @@ -162,14 +143,13 @@ static void _CallPythonObject(ctypes_state *st, StgInfo *info; if (PyStgInfo_FromType(st, cnv, &info) < 0) { - goto Done; + goto Error; } if (info && info->getfunc && !_ctypes_simple_instance(st, cnv)) { PyObject *v = info->getfunc(*pArgs, info->size); if (!v) { - PrintError("create argument %zd:\n", i); - goto Done; + goto Error; } args[i] = v; /* XXX XXX XX @@ -182,24 +162,25 @@ static void _CallPythonObject(ctypes_state *st, /* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */ CDataObject *obj = (CDataObject *)_PyObject_CallNoArgs(cnv); if (!obj) { - PrintError("create argument %zd:\n", i); - goto Done; + goto Error; } if (!CDataObject_Check(st, obj)) { + PyErr_Format(PyExc_TypeError, + "%R returned unexpected result of type %T", cnv, obj); Py_DECREF(obj); - PrintError("unexpected result of create argument %zd:\n", i); - goto Done; + goto Error; } memcpy(obj->b_ptr, *pArgs, info->size); args[i] = (PyObject *)obj; #ifdef MS_WIN32 - TryAddRef(cnv, obj); + if (TryAddRef(cnv, obj) < 0) { + goto Error; + } #endif } else { - PyErr_SetString(PyExc_TypeError, - "cannot build parameter"); - PrintError("Parsing argument %zd\n", i); - goto Done; + PyErr_Format(PyExc_TypeError, + "cannot build parameter of type %R", cnv); + goto Error; } /* XXX error handling! */ pArgs++; @@ -207,8 +188,13 @@ static void _CallPythonObject(ctypes_state *st, if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) { error_object = _ctypes_get_errobj(st, &space); - if (error_object == NULL) + if (error_object == NULL) { + PyErr_FormatUnraisable( + "Exception ignored while setting error for " + "ctypes callback function %R", + callable); goto Done; + } if (flags & FUNCFLAG_USE_ERRNO) { int temp = space[0]; space[0] = errno; @@ -295,6 +281,14 @@ static void _CallPythonObject(ctypes_state *st, for (j = 0; j < i; j++) { Py_DECREF(args[j]); } + return; + + Error: + PyErr_FormatUnraisable( + "Exception ignored while creating argument %zd for " + "ctypes callback function %R", + i, callable); + goto Done; } static void closure_fcn(ffi_cif *cif,