Skip to content

Commit 232dda6

Browse files
authored
bpo-40679: Fix _PyEval_EvalCode() crash if qualname is NULL (GH-20615)
If name is NULL, name is now set to co->co_name. If qualname is NULL, qualname is now set to name. qualname must not be NULL: it is used to build error messages. Cleanup also the code: declare variables where they are initialized. Rename "name" local variables to "varname" to avoid overriding "name" parameter.
1 parent b022e5c commit 232dda6

File tree

2 files changed

+37
-22
lines changed

2 files changed

+37
-22
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a ``_PyEval_EvalCode()`` crash if *qualname* argument is NULL.

Python/ceval.c

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4107,14 +4107,22 @@ _PyEval_EvalCode(PyThreadState *tstate,
41074107
{
41084108
assert(is_tstate_valid(tstate));
41094109

4110-
PyCodeObject* co = (PyCodeObject*)_co;
4111-
PyFrameObject *f;
4110+
PyCodeObject *co = (PyCodeObject*)_co;
4111+
4112+
if (!name) {
4113+
name = co->co_name;
4114+
}
4115+
assert(name != NULL);
4116+
assert(PyUnicode_Check(name));
4117+
4118+
if (!qualname) {
4119+
qualname = name;
4120+
}
4121+
assert(qualname != NULL);
4122+
assert(PyUnicode_Check(qualname));
4123+
41124124
PyObject *retval = NULL;
4113-
PyObject **fastlocals, **freevars;
4114-
PyObject *x, *u;
41154125
const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount;
4116-
Py_ssize_t i, j, n;
4117-
PyObject *kwdict;
41184126

41194127
if (globals == NULL) {
41204128
_PyErr_SetString(tstate, PyExc_SystemError,
@@ -4123,14 +4131,16 @@ _PyEval_EvalCode(PyThreadState *tstate,
41234131
}
41244132

41254133
/* Create the frame */
4126-
f = _PyFrame_New_NoTrack(tstate, co, globals, locals);
4134+
PyFrameObject *f = _PyFrame_New_NoTrack(tstate, co, globals, locals);
41274135
if (f == NULL) {
41284136
return NULL;
41294137
}
4130-
fastlocals = f->f_localsplus;
4131-
freevars = f->f_localsplus + co->co_nlocals;
4138+
PyObject **fastlocals = f->f_localsplus;
4139+
PyObject **freevars = f->f_localsplus + co->co_nlocals;
41324140

41334141
/* Create a dictionary for keyword parameters (**kwags) */
4142+
PyObject *kwdict;
4143+
Py_ssize_t i;
41344144
if (co->co_flags & CO_VARKEYWORDS) {
41354145
kwdict = PyDict_New();
41364146
if (kwdict == NULL)
@@ -4146,21 +4156,22 @@ _PyEval_EvalCode(PyThreadState *tstate,
41464156
}
41474157

41484158
/* Copy all positional arguments into local variables */
4159+
Py_ssize_t j, n;
41494160
if (argcount > co->co_argcount) {
41504161
n = co->co_argcount;
41514162
}
41524163
else {
41534164
n = argcount;
41544165
}
41554166
for (j = 0; j < n; j++) {
4156-
x = args[j];
4167+
PyObject *x = args[j];
41574168
Py_INCREF(x);
41584169
SETLOCAL(j, x);
41594170
}
41604171

41614172
/* Pack other positional arguments into the *args argument */
41624173
if (co->co_flags & CO_VARARGS) {
4163-
u = _PyTuple_FromArray(args + n, argcount - n);
4174+
PyObject *u = _PyTuple_FromArray(args + n, argcount - n);
41644175
if (u == NULL) {
41654176
goto fail;
41664177
}
@@ -4186,16 +4197,16 @@ _PyEval_EvalCode(PyThreadState *tstate,
41864197
normally interned this should almost always hit. */
41874198
co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item;
41884199
for (j = co->co_posonlyargcount; j < total_args; j++) {
4189-
PyObject *name = co_varnames[j];
4190-
if (name == keyword) {
4200+
PyObject *varname = co_varnames[j];
4201+
if (varname == keyword) {
41914202
goto kw_found;
41924203
}
41934204
}
41944205

41954206
/* Slow fallback, just in case */
41964207
for (j = co->co_posonlyargcount; j < total_args; j++) {
4197-
PyObject *name = co_varnames[j];
4198-
int cmp = PyObject_RichCompareBool( keyword, name, Py_EQ);
4208+
PyObject *varname = co_varnames[j];
4209+
int cmp = PyObject_RichCompareBool( keyword, varname, Py_EQ);
41994210
if (cmp > 0) {
42004211
goto kw_found;
42014212
}
@@ -4209,7 +4220,8 @@ _PyEval_EvalCode(PyThreadState *tstate,
42094220

42104221
if (co->co_posonlyargcount
42114222
&& positional_only_passed_as_keyword(tstate, co,
4212-
kwcount, kwnames, qualname))
4223+
kwcount, kwnames,
4224+
qualname))
42134225
{
42144226
goto fail;
42154227
}
@@ -4238,7 +4250,8 @@ _PyEval_EvalCode(PyThreadState *tstate,
42384250

42394251
/* Check the number of positional arguments */
42404252
if ((argcount > co->co_argcount) && !(co->co_flags & CO_VARARGS)) {
4241-
too_many_positional(tstate, co, argcount, defcount, fastlocals, qualname);
4253+
too_many_positional(tstate, co, argcount, defcount, fastlocals,
4254+
qualname);
42424255
goto fail;
42434256
}
42444257

@@ -4252,7 +4265,8 @@ _PyEval_EvalCode(PyThreadState *tstate,
42524265
}
42534266
}
42544267
if (missing) {
4255-
missing_arguments(tstate, co, missing, defcount, fastlocals, qualname);
4268+
missing_arguments(tstate, co, missing, defcount, fastlocals,
4269+
qualname);
42564270
goto fail;
42574271
}
42584272
if (n > m)
@@ -4272,12 +4286,11 @@ _PyEval_EvalCode(PyThreadState *tstate,
42724286
if (co->co_kwonlyargcount > 0) {
42734287
Py_ssize_t missing = 0;
42744288
for (i = co->co_argcount; i < total_args; i++) {
4275-
PyObject *name;
42764289
if (GETLOCAL(i) != NULL)
42774290
continue;
4278-
name = PyTuple_GET_ITEM(co->co_varnames, i);
4291+
PyObject *varname = PyTuple_GET_ITEM(co->co_varnames, i);
42794292
if (kwdefs != NULL) {
4280-
PyObject *def = PyDict_GetItemWithError(kwdefs, name);
4293+
PyObject *def = PyDict_GetItemWithError(kwdefs, varname);
42814294
if (def) {
42824295
Py_INCREF(def);
42834296
SETLOCAL(i, def);
@@ -4290,7 +4303,8 @@ _PyEval_EvalCode(PyThreadState *tstate,
42904303
missing++;
42914304
}
42924305
if (missing) {
4293-
missing_arguments(tstate, co, missing, -1, fastlocals, qualname);
4306+
missing_arguments(tstate, co, missing, -1, fastlocals,
4307+
qualname);
42944308
goto fail;
42954309
}
42964310
}

0 commit comments

Comments
 (0)