https://github.com/python/cpython/commit/a175da7a3ae3ba8d8aab9225c755bb41782edb4c
commit: a175da7a3ae3ba8d8aab9225c755bb41782edb4c
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-29T19:03:00+03:00
summary:

gh-155905: Fix error handling in _testcapi helpers (GH-155906)

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) <[email protected]>

files:
M Modules/_testcapi/object.c
M Modules/_testcapimodule.c

diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c
index 09a548fd2e2448..425ec540b62dcc 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, "<refcnt %zd at %p>",
                   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 fb18a866e62812..c01197d15bad5f 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;
     }
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to