From fd7763496285bc75e65ff32b64bd1a507d184844 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 16 Aug 2026 20:33:38 +0300 Subject: [PATCH] gh-155905: Fix error handling in _testcapi helpers Py_fopen() sets an exception and returns NULL on error. The pyobject_print*() helpers did not check the result and crashed, and the pymarshal_*() helpers set a second exception on top of it. The pyobject_print*() helpers which take a single argument now use METH_O, and the result of PyUnicode_FromString() is now checked. Co-Authored-By: Claude Opus 5 (1M context) --- Modules/_testcapi/object.c | 40 ++++++++++++++++++++++---------------- Modules/_testcapimodule.c | 6 ------ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c index 09a548fd2e24489..425ec540b62dcc0 100644 --- a/Modules/_testcapi/object.c +++ b/Modules/_testcapi/object.c @@ -16,6 +16,9 @@ call_pyobject_print(PyObject *self, PyObject * args) } fp = Py_fopen(filename, "w+"); + if (fp == NULL) { + return NULL; + } if (Py_IsTrue(print_raw)) { flags = Py_PRINT_RAW; @@ -32,17 +35,15 @@ call_pyobject_print(PyObject *self, PyObject * args) } static PyObject * -pyobject_print_null(PyObject *self, PyObject *args) +pyobject_print_null(PyObject *self, PyObject *filename) { - PyObject *filename; FILE *fp; - if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) { + fp = Py_fopen(filename, "w+"); + if (fp == NULL) { return NULL; } - fp = Py_fopen(filename, "w+"); - if (PyObject_Print(NULL, fp, 0) < 0) { fclose(fp); return NULL; @@ -54,26 +55,29 @@ pyobject_print_null(PyObject *self, PyObject *args) } static PyObject * -pyobject_print_noref_object(PyObject *self, PyObject *args) +pyobject_print_noref_object(PyObject *self, PyObject *filename) { PyObject *test_string; - PyObject *filename; FILE *fp; char correct_string[100]; test_string = PyUnicode_FromString("Spam spam spam"); + if (test_string == NULL) { + return NULL; + } Py_SET_REFCNT(test_string, 0); PyOS_snprintf(correct_string, 100, "", Py_REFCNT(test_string), (void *)test_string); - if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) { + fp = Py_fopen(filename, "w+"); + if (fp == NULL) { + Py_SET_REFCNT(test_string, 1); + Py_DECREF(test_string); return NULL; } - fp = Py_fopen(filename, "w+"); - if (PyObject_Print(test_string, fp, 0) < 0){ fclose(fp); Py_SET_REFCNT(test_string, 1); @@ -90,20 +94,22 @@ pyobject_print_noref_object(PyObject *self, PyObject *args) } static PyObject * -pyobject_print_os_error(PyObject *self, PyObject *args) +pyobject_print_os_error(PyObject *self, PyObject *filename) { PyObject *test_string; - PyObject *filename; FILE *fp; test_string = PyUnicode_FromString("Spam spam spam"); - - if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) { + if (test_string == NULL) { return NULL; } // open file in read mode to induce OSError fp = Py_fopen(filename, "r"); + if (fp == NULL) { + Py_DECREF(test_string); + return NULL; + } if (PyObject_Print(test_string, fp, 0) < 0) { fclose(fp); @@ -582,9 +588,9 @@ pysentinel_checkexact(PyObject *self, PyObject *obj) static PyMethodDef test_methods[] = { {"call_pyobject_print", call_pyobject_print, METH_VARARGS}, - {"pyobject_print_null", pyobject_print_null, METH_VARARGS}, - {"pyobject_print_noref_object", pyobject_print_noref_object, METH_VARARGS}, - {"pyobject_print_os_error", pyobject_print_os_error, METH_VARARGS}, + {"pyobject_print_null", pyobject_print_null, METH_O}, + {"pyobject_print_noref_object", pyobject_print_noref_object, METH_O}, + {"pyobject_print_os_error", pyobject_print_os_error, METH_O}, {"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O}, {"pyobject_enable_deferred_refcount", pyobject_enable_deferred_refcount, METH_O}, {"pyobject_is_unique_temporary", pyobject_is_unique_temporary, METH_O}, diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index fb18a866e628128..c01197d15bad5f0 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1431,7 +1431,6 @@ pymarshal_write_long_to_file(PyObject* self, PyObject *args) fp = Py_fopen(filename, "wb"); if (fp == NULL) { - PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -1456,7 +1455,6 @@ pymarshal_write_object_to_file(PyObject* self, PyObject *args) fp = Py_fopen(filename, "wb"); if (fp == NULL) { - PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -1480,7 +1478,6 @@ pymarshal_read_short_from_file(PyObject* self, PyObject *args) fp = Py_fopen(filename, "rb"); if (fp == NULL) { - PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -1505,7 +1502,6 @@ pymarshal_read_long_from_file(PyObject* self, PyObject *args) fp = Py_fopen(filename, "rb"); if (fp == NULL) { - PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -1527,7 +1523,6 @@ pymarshal_read_last_object_from_file(PyObject* self, PyObject *args) FILE *fp = Py_fopen(filename, "rb"); if (fp == NULL) { - PyErr_SetFromErrno(PyExc_OSError); return NULL; } @@ -1550,7 +1545,6 @@ pymarshal_read_object_from_file(PyObject* self, PyObject *args) FILE *fp = Py_fopen(filename, "rb"); if (fp == NULL) { - PyErr_SetFromErrno(PyExc_OSError); return NULL; }