Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 13e602e

Browse files
Issue #26168: Fixed possible refleaks in failing Py_BuildValue() with the "N"
format unit.
1 parent 3dc5129 commit 13e602e

4 files changed

Lines changed: 171 additions & 53 deletions

File tree

Lib/test/test_capi.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ def test_return_result_with_error(self):
236236
'return_result_with_error.* '
237237
'returned a result with an error set')
238238

239+
def test_buildvalue_N(self):
240+
_testcapi.test_buildvalue_N()
241+
239242

240243
@unittest.skipUnless(threading, 'Threading required for this test.')
241244
class TestPendingCalls(unittest.TestCase):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ Release date: tba
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #26168: Fixed possible refleaks in failing Py_BuildValue() with the "N"
14+
format unit.
15+
1316
- Issue #26991: Fix possible refleak when creating a function with annotations.
1417

1518
- Issue #27039: Fixed bytearray.remove() for values greater than 127. Patch by

Modules/_testcapimodule.c

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,100 @@ test_L_code(PyObject *self)
872872

873873
#endif /* ifdef HAVE_LONG_LONG */
874874

875+
static PyObject *
876+
return_none(void *unused)
877+
{
878+
Py_RETURN_NONE;
879+
}
880+
881+
static PyObject *
882+
raise_error(void *unused)
883+
{
884+
PyErr_SetNone(PyExc_ValueError);
885+
return NULL;
886+
}
887+
888+
static int
889+
test_buildvalue_N_error(const char *fmt)
890+
{
891+
PyObject *arg, *res;
892+
893+
arg = PyList_New(0);
894+
if (arg == NULL) {
895+
return -1;
896+
}
897+
898+
Py_INCREF(arg);
899+
res = Py_BuildValue(fmt, return_none, NULL, arg);
900+
if (res == NULL) {
901+
return -1;
902+
}
903+
Py_DECREF(res);
904+
if (Py_REFCNT(arg) != 1) {
905+
PyErr_Format(TestError, "test_buildvalue_N: "
906+
"arg was not decrefed in successful "
907+
"Py_BuildValue(\"%s\")", fmt);
908+
return -1;
909+
}
910+
911+
Py_INCREF(arg);
912+
res = Py_BuildValue(fmt, raise_error, NULL, arg);
913+
if (res != NULL || !PyErr_Occurred()) {
914+
PyErr_Format(TestError, "test_buildvalue_N: "
915+
"Py_BuildValue(\"%s\") didn't complain", fmt);
916+
return -1;
917+
}
918+
PyErr_Clear();
919+
if (Py_REFCNT(arg) != 1) {
920+
PyErr_Format(TestError, "test_buildvalue_N: "
921+
"arg was not decrefed in failed "
922+
"Py_BuildValue(\"%s\")", fmt);
923+
return -1;
924+
}
925+
Py_DECREF(arg);
926+
return 0;
927+
}
928+
929+
static PyObject *
930+
test_buildvalue_N(PyObject *self, PyObject *noargs)
931+
{
932+
PyObject *arg, *res;
933+
934+
arg = PyList_New(0);
935+
if (arg == NULL) {
936+
return NULL;
937+
}
938+
Py_INCREF(arg);
939+
res = Py_BuildValue("N", arg);
940+
if (res == NULL) {
941+
return NULL;
942+
}
943+
if (res != arg) {
944+
return raiseTestError("test_buildvalue_N",
945+
"Py_BuildValue(\"N\") returned wrong result");
946+
}
947+
if (Py_REFCNT(arg) != 2) {
948+
return raiseTestError("test_buildvalue_N",
949+
"arg was not decrefed in Py_BuildValue(\"N\")");
950+
}
951+
Py_DECREF(res);
952+
Py_DECREF(arg);
953+
954+
if (test_buildvalue_N_error("O&N") < 0)
955+
return NULL;
956+
if (test_buildvalue_N_error("(O&N)") < 0)
957+
return NULL;
958+
if (test_buildvalue_N_error("[O&N]") < 0)
959+
return NULL;
960+
if (test_buildvalue_N_error("{O&N}") < 0)
961+
return NULL;
962+
if (test_buildvalue_N_error("{()O&(())N}") < 0)
963+
return NULL;
964+
965+
Py_RETURN_NONE;
966+
}
967+
968+
875969
static PyObject *
876970
get_args(PyObject *self, PyObject *args)
877971
{
@@ -3728,6 +3822,7 @@ static PyMethodDef TestMethods[] = {
37283822
{"test_pep3118_obsolete_write_locks", (PyCFunction)test_pep3118_obsolete_write_locks, METH_NOARGS},
37293823
#endif
37303824
{"getbuffer_with_null_view", getbuffer_with_null_view, METH_O},
3825+
{"test_buildvalue_N", test_buildvalue_N, METH_NOARGS},
37313826
{"get_args", get_args, METH_VARARGS},
37323827
{"get_kwargs", (PyCFunction)get_kwargs, METH_VARARGS|METH_KEYWORDS},
37333828
{"getargs_tuple", getargs_tuple, METH_VARARGS},

Python/modsupport.c

Lines changed: 70 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -63,48 +63,84 @@ static PyObject *do_mkdict(const char**, va_list *, int, int, int);
6363
static PyObject *do_mkvalue(const char**, va_list *, int);
6464

6565

66+
static void
67+
do_ignore(const char **p_format, va_list *p_va, int endchar, int n, int flags)
68+
{
69+
PyObject *v;
70+
int i;
71+
assert(PyErr_Occurred());
72+
v = PyTuple_New(n);
73+
for (i = 0; i < n; i++) {
74+
PyObject *exception, *value, *tb, *w;
75+
76+
PyErr_Fetch(&exception, &value, &tb);
77+
w = do_mkvalue(p_format, p_va, flags);
78+
PyErr_Restore(exception, value, tb);
79+
if (w != NULL) {
80+
if (v != NULL) {
81+
PyTuple_SET_ITEM(v, i, w);
82+
}
83+
else {
84+
Py_DECREF(w);
85+
}
86+
}
87+
}
88+
Py_XDECREF(v);
89+
if (**p_format != endchar) {
90+
PyErr_SetString(PyExc_SystemError,
91+
"Unmatched paren in format");
92+
return;
93+
}
94+
if (endchar)
95+
++*p_format;
96+
}
97+
6698
static PyObject *
6799
do_mkdict(const char **p_format, va_list *p_va, int endchar, int n, int flags)
68100
{
69101
PyObject *d;
70102
int i;
71-
int itemfailed = 0;
72103
if (n < 0)
73104
return NULL;
74-
if ((d = PyDict_New()) == NULL)
105+
if (n % 2) {
106+
PyErr_SetString(PyExc_SystemError,
107+
"Bad dict format");
108+
do_ignore(p_format, p_va, endchar, n, flags);
75109
return NULL;
110+
}
76111
/* Note that we can't bail immediately on error as this will leak
77112
refcounts on any 'N' arguments. */
113+
if ((d = PyDict_New()) == NULL) {
114+
do_ignore(p_format, p_va, endchar, n, flags);
115+
return NULL;
116+
}
78117
for (i = 0; i < n; i+= 2) {
79118
PyObject *k, *v;
80-
int err;
119+
81120
k = do_mkvalue(p_format, p_va, flags);
82121
if (k == NULL) {
83-
itemfailed = 1;
84-
Py_INCREF(Py_None);
85-
k = Py_None;
122+
do_ignore(p_format, p_va, endchar, n - i - 1, flags);
123+
Py_DECREF(d);
124+
return NULL;
86125
}
87126
v = do_mkvalue(p_format, p_va, flags);
88-
if (v == NULL) {
89-
itemfailed = 1;
90-
Py_INCREF(Py_None);
91-
v = Py_None;
92-
}
93-
err = PyDict_SetItem(d, k, v);
94-
Py_DECREF(k);
95-
Py_DECREF(v);
96-
if (err < 0 || itemfailed) {
127+
if (v == NULL || PyDict_SetItem(d, k, v) < 0) {
128+
do_ignore(p_format, p_va, endchar, n - i - 2, flags);
129+
Py_DECREF(k);
130+
Py_XDECREF(v);
97131
Py_DECREF(d);
98132
return NULL;
99133
}
134+
Py_DECREF(k);
135+
Py_DECREF(v);
100136
}
101-
if (d != NULL && **p_format != endchar) {
137+
if (**p_format != endchar) {
102138
Py_DECREF(d);
103-
d = NULL;
104139
PyErr_SetString(PyExc_SystemError,
105140
"Unmatched paren in format");
141+
return NULL;
106142
}
107-
else if (endchar)
143+
if (endchar)
108144
++*p_format;
109145
return d;
110146
}
@@ -114,29 +150,24 @@ do_mklist(const char **p_format, va_list *p_va, int endchar, int n, int flags)
114150
{
115151
PyObject *v;
116152
int i;
117-
int itemfailed = 0;
118153
if (n < 0)
119154
return NULL;
120-
v = PyList_New(n);
121-
if (v == NULL)
122-
return NULL;
123155
/* Note that we can't bail immediately on error as this will leak
124156
refcounts on any 'N' arguments. */
157+
v = PyList_New(n);
158+
if (v == NULL) {
159+
do_ignore(p_format, p_va, endchar, n, flags);
160+
return NULL;
161+
}
125162
for (i = 0; i < n; i++) {
126163
PyObject *w = do_mkvalue(p_format, p_va, flags);
127164
if (w == NULL) {
128-
itemfailed = 1;
129-
Py_INCREF(Py_None);
130-
w = Py_None;
165+
do_ignore(p_format, p_va, endchar, n - i - 1, flags);
166+
Py_DECREF(v);
167+
return NULL;
131168
}
132169
PyList_SET_ITEM(v, i, w);
133170
}
134-
135-
if (itemfailed) {
136-
/* do_mkvalue() should have already set an error */
137-
Py_DECREF(v);
138-
return NULL;
139-
}
140171
if (**p_format != endchar) {
141172
Py_DECREF(v);
142173
PyErr_SetString(PyExc_SystemError,
@@ -153,37 +184,23 @@ do_mktuple(const char **p_format, va_list *p_va, int endchar, int n, int flags)
153184
{
154185
PyObject *v;
155186
int i;
156-
int itemfailed = 0;
157187
if (n < 0)
158188
return NULL;
159-
if ((v = PyTuple_New(n)) == NULL)
160-
return NULL;
161189
/* Note that we can't bail immediately on error as this will leak
162190
refcounts on any 'N' arguments. */
191+
if ((v = PyTuple_New(n)) == NULL) {
192+
do_ignore(p_format, p_va, endchar, n, flags);
193+
return NULL;
194+
}
163195
for (i = 0; i < n; i++) {
164-
PyObject *w;
165-
166-
if (itemfailed) {
167-
PyObject *exception, *value, *tb;
168-
PyErr_Fetch(&exception, &value, &tb);
169-
w = do_mkvalue(p_format, p_va, flags);
170-
PyErr_Restore(exception, value, tb);
171-
}
172-
else {
173-
w = do_mkvalue(p_format, p_va, flags);
174-
}
196+
PyObject *w = do_mkvalue(p_format, p_va, flags);
175197
if (w == NULL) {
176-
itemfailed = 1;
177-
Py_INCREF(Py_None);
178-
w = Py_None;
198+
do_ignore(p_format, p_va, endchar, n - i - 1, flags);
199+
Py_DECREF(v);
200+
return NULL;
179201
}
180202
PyTuple_SET_ITEM(v, i, w);
181203
}
182-
if (itemfailed) {
183-
/* do_mkvalue() should have already set an error */
184-
Py_DECREF(v);
185-
return NULL;
186-
}
187204
if (**p_format != endchar) {
188205
Py_DECREF(v);
189206
PyErr_SetString(PyExc_SystemError,

0 commit comments

Comments
 (0)