Skip to content

Commit a175da7

Browse files
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>
1 parent 5cb7c39 commit a175da7

2 files changed

Lines changed: 23 additions & 23 deletions

File tree

Modules/_testcapi/object.c

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ call_pyobject_print(PyObject *self, PyObject * args)
1616
}
1717

1818
fp = Py_fopen(filename, "w+");
19+
if (fp == NULL) {
20+
return NULL;
21+
}
1922

2023
if (Py_IsTrue(print_raw)) {
2124
flags = Py_PRINT_RAW;
@@ -32,17 +35,15 @@ call_pyobject_print(PyObject *self, PyObject * args)
3235
}
3336

3437
static PyObject *
35-
pyobject_print_null(PyObject *self, PyObject *args)
38+
pyobject_print_null(PyObject *self, PyObject *filename)
3639
{
37-
PyObject *filename;
3840
FILE *fp;
3941

40-
if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) {
42+
fp = Py_fopen(filename, "w+");
43+
if (fp == NULL) {
4144
return NULL;
4245
}
4346

44-
fp = Py_fopen(filename, "w+");
45-
4647
if (PyObject_Print(NULL, fp, 0) < 0) {
4748
fclose(fp);
4849
return NULL;
@@ -54,26 +55,29 @@ pyobject_print_null(PyObject *self, PyObject *args)
5455
}
5556

5657
static PyObject *
57-
pyobject_print_noref_object(PyObject *self, PyObject *args)
58+
pyobject_print_noref_object(PyObject *self, PyObject *filename)
5859
{
5960
PyObject *test_string;
60-
PyObject *filename;
6161
FILE *fp;
6262
char correct_string[100];
6363

6464
test_string = PyUnicode_FromString("Spam spam spam");
65+
if (test_string == NULL) {
66+
return NULL;
67+
}
6568

6669
Py_SET_REFCNT(test_string, 0);
6770

6871
PyOS_snprintf(correct_string, 100, "<refcnt %zd at %p>",
6972
Py_REFCNT(test_string), (void *)test_string);
7073

71-
if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) {
74+
fp = Py_fopen(filename, "w+");
75+
if (fp == NULL) {
76+
Py_SET_REFCNT(test_string, 1);
77+
Py_DECREF(test_string);
7278
return NULL;
7379
}
7480

75-
fp = Py_fopen(filename, "w+");
76-
7781
if (PyObject_Print(test_string, fp, 0) < 0){
7882
fclose(fp);
7983
Py_SET_REFCNT(test_string, 1);
@@ -90,20 +94,22 @@ pyobject_print_noref_object(PyObject *self, PyObject *args)
9094
}
9195

9296
static PyObject *
93-
pyobject_print_os_error(PyObject *self, PyObject *args)
97+
pyobject_print_os_error(PyObject *self, PyObject *filename)
9498
{
9599
PyObject *test_string;
96-
PyObject *filename;
97100
FILE *fp;
98101

99102
test_string = PyUnicode_FromString("Spam spam spam");
100-
101-
if (!PyArg_UnpackTuple(args, "call_pyobject_print", 1, 1, &filename)) {
103+
if (test_string == NULL) {
102104
return NULL;
103105
}
104106

105107
// open file in read mode to induce OSError
106108
fp = Py_fopen(filename, "r");
109+
if (fp == NULL) {
110+
Py_DECREF(test_string);
111+
return NULL;
112+
}
107113

108114
if (PyObject_Print(test_string, fp, 0) < 0) {
109115
fclose(fp);
@@ -582,9 +588,9 @@ pysentinel_checkexact(PyObject *self, PyObject *obj)
582588

583589
static PyMethodDef test_methods[] = {
584590
{"call_pyobject_print", call_pyobject_print, METH_VARARGS},
585-
{"pyobject_print_null", pyobject_print_null, METH_VARARGS},
586-
{"pyobject_print_noref_object", pyobject_print_noref_object, METH_VARARGS},
587-
{"pyobject_print_os_error", pyobject_print_os_error, METH_VARARGS},
591+
{"pyobject_print_null", pyobject_print_null, METH_O},
592+
{"pyobject_print_noref_object", pyobject_print_noref_object, METH_O},
593+
{"pyobject_print_os_error", pyobject_print_os_error, METH_O},
588594
{"pyobject_clear_weakrefs_no_callbacks", pyobject_clear_weakrefs_no_callbacks, METH_O},
589595
{"pyobject_enable_deferred_refcount", pyobject_enable_deferred_refcount, METH_O},
590596
{"pyobject_is_unique_temporary", pyobject_is_unique_temporary, METH_O},

Modules/_testcapimodule.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,7 +1431,6 @@ pymarshal_write_long_to_file(PyObject* self, PyObject *args)
14311431

14321432
fp = Py_fopen(filename, "wb");
14331433
if (fp == NULL) {
1434-
PyErr_SetFromErrno(PyExc_OSError);
14351434
return NULL;
14361435
}
14371436

@@ -1456,7 +1455,6 @@ pymarshal_write_object_to_file(PyObject* self, PyObject *args)
14561455

14571456
fp = Py_fopen(filename, "wb");
14581457
if (fp == NULL) {
1459-
PyErr_SetFromErrno(PyExc_OSError);
14601458
return NULL;
14611459
}
14621460

@@ -1480,7 +1478,6 @@ pymarshal_read_short_from_file(PyObject* self, PyObject *args)
14801478

14811479
fp = Py_fopen(filename, "rb");
14821480
if (fp == NULL) {
1483-
PyErr_SetFromErrno(PyExc_OSError);
14841481
return NULL;
14851482
}
14861483

@@ -1505,7 +1502,6 @@ pymarshal_read_long_from_file(PyObject* self, PyObject *args)
15051502

15061503
fp = Py_fopen(filename, "rb");
15071504
if (fp == NULL) {
1508-
PyErr_SetFromErrno(PyExc_OSError);
15091505
return NULL;
15101506
}
15111507

@@ -1527,7 +1523,6 @@ pymarshal_read_last_object_from_file(PyObject* self, PyObject *args)
15271523

15281524
FILE *fp = Py_fopen(filename, "rb");
15291525
if (fp == NULL) {
1530-
PyErr_SetFromErrno(PyExc_OSError);
15311526
return NULL;
15321527
}
15331528

@@ -1550,7 +1545,6 @@ pymarshal_read_object_from_file(PyObject* self, PyObject *args)
15501545

15511546
FILE *fp = Py_fopen(filename, "rb");
15521547
if (fp == NULL) {
1553-
PyErr_SetFromErrno(PyExc_OSError);
15541548
return NULL;
15551549
}
15561550

0 commit comments

Comments
 (0)