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) <noreply@anthropic.com>
diff --git a/Modules/_testcapi/object.c b/Modules/_testcapi/object.c
index 09a548f..425ec54 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 fb18a86..c01197d 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;
}