mirror of
https://github.com/python/cpython.git
synced 2025-08-22 17:55:18 +00:00
gh-94938: Fix errror detection of unexpected keyword arguments (GH-94999)
When keyword argument name is an instance of a str subclass with overloaded methods __eq__ and __hash__, the former code could not find the name of an extraneous keyword argument to report an error, and _PyArg_UnpackKeywords() returned success without setting the corresponding cell in the linearized arguments array. But since the number of expected initialized cells is determined as the total number of passed arguments, this lead to reading NULL as a keyword parameter value, that caused SystemError or crash or other undesired behavior.
This commit is contained in:
parent
0fe645d6fd
commit
ebad53a4dc
4 changed files with 112 additions and 85 deletions
142
Python/getargs.c
142
Python/getargs.c
|
@ -1502,6 +1502,50 @@ _PyArg_VaParseTupleAndKeywordsFast_SizeT(PyObject *args, PyObject *keywords,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
error_unexpected_keyword_arg(PyObject *kwargs, PyObject *kwnames, PyObject *kwtuple, const char *fname)
|
||||
{
|
||||
/* make sure there are no extraneous keyword arguments */
|
||||
Py_ssize_t j = 0;
|
||||
while (1) {
|
||||
PyObject *keyword;
|
||||
if (kwargs != NULL) {
|
||||
if (!PyDict_Next(kwargs, &j, &keyword, NULL))
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (j >= PyTuple_GET_SIZE(kwnames))
|
||||
break;
|
||||
keyword = PyTuple_GET_ITEM(kwnames, j);
|
||||
j++;
|
||||
}
|
||||
if (!PyUnicode_Check(keyword)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"keywords must be strings");
|
||||
return;
|
||||
}
|
||||
|
||||
int match = PySequence_Contains(kwtuple, keyword);
|
||||
if (match <= 0) {
|
||||
if (!match) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%S' is an invalid keyword "
|
||||
"argument for %.200s%s",
|
||||
keyword,
|
||||
(fname == NULL) ? "this function" : fname,
|
||||
(fname == NULL) ? "" : "()");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Something wrong happened. There are extraneous keyword arguments,
|
||||
* but we don't know what. And we don't bother. */
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"invalid keyword argument for %.200s%s",
|
||||
(fname == NULL) ? "this function" : fname,
|
||||
(fname == NULL) ? "" : "()");
|
||||
}
|
||||
|
||||
int
|
||||
PyArg_ValidateKeywordArguments(PyObject *kwargs)
|
||||
{
|
||||
|
@ -1790,6 +1834,13 @@ vgetargskeywords(PyObject *args, PyObject *kwargs, const char *format,
|
|||
return cleanreturn(0, &freelist);
|
||||
}
|
||||
}
|
||||
/* Something wrong happened. There are extraneous keyword arguments,
|
||||
* but we don't know what. And we don't bother. */
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"invalid keyword argument for %.200s%s",
|
||||
(fname == NULL) ? "this function" : fname,
|
||||
(fname == NULL) ? "" : "()");
|
||||
return cleanreturn(0, &freelist);
|
||||
}
|
||||
|
||||
return cleanreturn(1, &freelist);
|
||||
|
@ -2132,7 +2183,6 @@ vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs,
|
|||
assert(IS_END_OF_FORMAT(*format) || (*format == '|') || (*format == '$'));
|
||||
|
||||
if (nkwargs > 0) {
|
||||
Py_ssize_t j;
|
||||
/* make sure there are no arguments given by name and position */
|
||||
for (i = pos; i < nargs; i++) {
|
||||
keyword = PyTuple_GET_ITEM(kwtuple, i - pos);
|
||||
|
@ -2156,34 +2206,9 @@ vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs,
|
|||
return cleanreturn(0, &freelist);
|
||||
}
|
||||
}
|
||||
/* make sure there are no extraneous keyword arguments */
|
||||
j = 0;
|
||||
while (1) {
|
||||
int match;
|
||||
if (kwargs != NULL) {
|
||||
if (!PyDict_Next(kwargs, &j, &keyword, NULL))
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (j >= PyTuple_GET_SIZE(kwnames))
|
||||
break;
|
||||
keyword = PyTuple_GET_ITEM(kwnames, j);
|
||||
j++;
|
||||
}
|
||||
|
||||
match = PySequence_Contains(kwtuple, keyword);
|
||||
if (match <= 0) {
|
||||
if (!match) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%S' is an invalid keyword "
|
||||
"argument for %.200s%s",
|
||||
keyword,
|
||||
(parser->fname == NULL) ? "this function" : parser->fname,
|
||||
(parser->fname == NULL) ? "" : "()");
|
||||
}
|
||||
return cleanreturn(0, &freelist);
|
||||
}
|
||||
}
|
||||
error_unexpected_keyword_arg(kwargs, kwnames, kwtuple, parser->fname);
|
||||
return cleanreturn(0, &freelist);
|
||||
}
|
||||
|
||||
return cleanreturn(1, &freelist);
|
||||
|
@ -2357,7 +2382,6 @@ _PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs,
|
|||
}
|
||||
|
||||
if (nkwargs > 0) {
|
||||
Py_ssize_t j;
|
||||
/* make sure there are no arguments given by name and position */
|
||||
for (i = posonly; i < nargs; i++) {
|
||||
keyword = PyTuple_GET_ITEM(kwtuple, i - posonly);
|
||||
|
@ -2381,34 +2405,9 @@ _PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs,
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
/* make sure there are no extraneous keyword arguments */
|
||||
j = 0;
|
||||
while (1) {
|
||||
int match;
|
||||
if (kwargs != NULL) {
|
||||
if (!PyDict_Next(kwargs, &j, &keyword, NULL))
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (j >= PyTuple_GET_SIZE(kwnames))
|
||||
break;
|
||||
keyword = PyTuple_GET_ITEM(kwnames, j);
|
||||
j++;
|
||||
}
|
||||
|
||||
match = PySequence_Contains(kwtuple, keyword);
|
||||
if (match <= 0) {
|
||||
if (!match) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%S' is an invalid keyword "
|
||||
"argument for %.200s%s",
|
||||
keyword,
|
||||
(parser->fname == NULL) ? "this function" : parser->fname,
|
||||
(parser->fname == NULL) ? "" : "()");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
error_unexpected_keyword_arg(kwargs, kwnames, kwtuple, parser->fname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buf;
|
||||
|
@ -2537,35 +2536,8 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs,
|
|||
}
|
||||
|
||||
if (nkwargs > 0) {
|
||||
Py_ssize_t j;
|
||||
/* make sure there are no extraneous keyword arguments */
|
||||
j = 0;
|
||||
while (1) {
|
||||
int match;
|
||||
if (kwargs != NULL) {
|
||||
if (!PyDict_Next(kwargs, &j, &keyword, NULL))
|
||||
break;
|
||||
}
|
||||
else {
|
||||
if (j >= PyTuple_GET_SIZE(kwnames))
|
||||
break;
|
||||
keyword = PyTuple_GET_ITEM(kwnames, j);
|
||||
j++;
|
||||
}
|
||||
|
||||
match = PySequence_Contains(kwtuple, keyword);
|
||||
if (match <= 0) {
|
||||
if (!match) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"'%S' is an invalid keyword "
|
||||
"argument for %.200s%s",
|
||||
keyword,
|
||||
(parser->fname == NULL) ? "this function" : parser->fname,
|
||||
(parser->fname == NULL) ? "" : "()");
|
||||
}
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
error_unexpected_keyword_arg(kwargs, kwnames, kwtuple, parser->fname);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
return buf;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue