Skip to content

Commit

Permalink
[3.11] gh-64490: Fix bugs in argument clinic varargs processing (GH-3…
Browse files Browse the repository at this point in the history
…2092) (#100368)

(cherry picked from commit 0da7283)
  • Loading branch information
colorfulappl authored Dec 28, 2022
1 parent 18b43cf commit a3dbd4c
Show file tree
Hide file tree
Showing 7 changed files with 449 additions and 11 deletions.
7 changes: 3 additions & 4 deletions Lib/test/clinic.test
Original file line number Diff line number Diff line change
Expand Up @@ -3368,7 +3368,6 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
static const char * const _keywords[] = {"a", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg", 0};
PyObject *argsbuf[2];
Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
PyObject *a;
PyObject *__clinic_args = NULL;

Expand All @@ -3387,7 +3386,7 @@ exit:

static PyObject *
test_vararg_impl(PyObject *module, PyObject *a, PyObject *args)
/*[clinic end generated code: output=a2baf8c1fade41d2 input=81d33815ad1bae6e]*/
/*[clinic end generated code: output=ce9334333757f6ea input=81d33815ad1bae6e]*/

/*[clinic input]
test_vararg_with_default
Expand Down Expand Up @@ -3418,7 +3417,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar
static const char * const _keywords[] = {"a", "b", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg_with_default", 0};
PyObject *argsbuf[3];
Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
PyObject *a;
PyObject *__clinic_args = NULL;
int b = 0;
Expand Down Expand Up @@ -3447,7 +3446,7 @@ exit:
static PyObject *
test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args,
int b)
/*[clinic end generated code: output=3821d282c29f8616 input=6e110b54acd9b22d]*/
/*[clinic end generated code: output=32fb19dd6bcf9185 input=6e110b54acd9b22d]*/

/*[clinic input]
test_vararg_with_only_defaults
Expand Down
44 changes: 44 additions & 0 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,15 @@ def test_parameters_not_permitted_after_slash_for_now(self):
x: int
""")

def test_parameters_no_more_than_one_vararg(self):
s = self.parse_function_should_fail("""
module foo
foo.bar
*vararg1: object
*vararg2: object
""")
self.assertEqual(s, "Error on line 0:\nToo many var args\n")

def test_function_not_at_column_0(self):
function = self.parse_function("""
module foo
Expand Down Expand Up @@ -1222,13 +1231,47 @@ def test_keyword_only_parameter(self):
ac_tester.keyword_only_parameter(1)
self.assertEqual(ac_tester.keyword_only_parameter(a=1), (1,))

def test_posonly_vararg(self):
with self.assertRaises(TypeError):
ac_tester.posonly_vararg()
self.assertEqual(ac_tester.posonly_vararg(1, 2), (1, 2, ()))
self.assertEqual(ac_tester.posonly_vararg(1, b=2), (1, 2, ()))
self.assertEqual(ac_tester.posonly_vararg(1, 2, 3, 4), (1, 2, (3, 4)))

def test_vararg_and_posonly(self):
with self.assertRaises(TypeError):
ac_tester.vararg_and_posonly()
with self.assertRaises(TypeError):
ac_tester.vararg_and_posonly(1, b=2)
self.assertEqual(ac_tester.vararg_and_posonly(1, 2, 3, 4), (1, (2, 3, 4)))

def test_vararg(self):
with self.assertRaises(TypeError):
ac_tester.vararg()
with self.assertRaises(TypeError):
ac_tester.vararg(1, b=2)
self.assertEqual(ac_tester.vararg(1, 2, 3, 4), (1, (2, 3, 4)))

def test_vararg_with_default(self):
with self.assertRaises(TypeError):
ac_tester.vararg_with_default()
self.assertEqual(ac_tester.vararg_with_default(1, b=False), (1, (), False))
self.assertEqual(ac_tester.vararg_with_default(1, 2, 3, 4), (1, (2, 3, 4), False))
self.assertEqual(ac_tester.vararg_with_default(1, 2, 3, 4, b=True), (1, (2, 3, 4), True))

def test_vararg_with_only_defaults(self):
self.assertEqual(ac_tester.vararg_with_only_defaults(), ((), None))
self.assertEqual(ac_tester.vararg_with_only_defaults(b=2), ((), 2))
self.assertEqual(ac_tester.vararg_with_only_defaults(1, b=2), ((1, ), 2))
self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4), ((1, 2, 3, 4), None))
self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4, b=5), ((1, 2, 3, 4), 5))

def test_gh_32092_oob(self):
ac_tester.gh_32092_oob(1, 2, 3, 4, kw1=5, kw2=6)

def test_gh_32092_kw_pass(self):
ac_tester.gh_32092_kw_pass(1, 2, 3)

def test_gh_99233_refcount(self):
arg = '*A unique string is not referenced by anywhere else.*'
arg_refcount_origin = sys.getrefcount(arg)
Expand All @@ -1241,5 +1284,6 @@ def test_gh_99240_double_free(self):
with self.assertRaisesRegex(TypeError, expected_error):
ac_tester.gh_99240_double_free('a', '\0b')


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Argument Clinic varargs bugfixes

* Fix out-of-bounds error in :c:func:`!_PyArg_UnpackKeywordsWithVararg`.
* Fix incorrect check which allowed more than one varargs in clinic.py.
* Fix miscalculation of ``noptargs`` in generated code.
* Do not generate ``noptargs`` when there is a vararg argument and no optional argument.

119 changes: 119 additions & 0 deletions Modules/_testclinic.c
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,25 @@ keyword_only_parameter_impl(PyObject *module, PyObject *a)
}


/*[clinic input]
posonly_vararg
a: object
/
b: object
*args: object
[clinic start generated code]*/

static PyObject *
posonly_vararg_impl(PyObject *module, PyObject *a, PyObject *b,
PyObject *args)
/*[clinic end generated code: output=ee6713acda6b954e input=783427fe7ec2b67a]*/
{
return pack_arguments_newref(3, a, b, args);
}


/*[clinic input]
vararg_and_posonly
Expand All @@ -967,6 +986,100 @@ vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args)
}


/*[clinic input]
vararg
a: object
*args: object
[clinic start generated code]*/

static PyObject *
vararg_impl(PyObject *module, PyObject *a, PyObject *args)
/*[clinic end generated code: output=91ab7a0efc52dd5e input=02c0f772d05f591e]*/
{
return pack_arguments_newref(2, a, args);
}


/*[clinic input]
vararg_with_default
a: object
*args: object
b: bool = False
[clinic start generated code]*/

static PyObject *
vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args,
int b)
/*[clinic end generated code: output=182c01035958ce92 input=68cafa6a79f89e36]*/
{
PyObject *obj_b = b ? Py_True : Py_False;
return pack_arguments_newref(3, a, args, obj_b);
}


/*[clinic input]
vararg_with_only_defaults
*args: object
b: object = None
[clinic start generated code]*/

static PyObject *
vararg_with_only_defaults_impl(PyObject *module, PyObject *args, PyObject *b)
/*[clinic end generated code: output=c06b1826d91f2f7b input=678c069bc67550e1]*/
{
return pack_arguments_newref(2, args, b);
}



/*[clinic input]
gh_32092_oob
pos1: object
pos2: object
*varargs: object
kw1: object = None
kw2: object = None
Proof-of-concept of GH-32092 OOB bug.
[clinic start generated code]*/

static PyObject *
gh_32092_oob_impl(PyObject *module, PyObject *pos1, PyObject *pos2,
PyObject *varargs, PyObject *kw1, PyObject *kw2)
/*[clinic end generated code: output=ee259c130054653f input=46d15c881608f8ff]*/
{
Py_RETURN_NONE;
}


/*[clinic input]
gh_32092_kw_pass
pos: object
*args: object
kw: object = None
Proof-of-concept of GH-32092 keyword args passing bug.
[clinic start generated code]*/

static PyObject *
gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, PyObject *args,
PyObject *kw)
/*[clinic end generated code: output=4a2bbe4f7c8604e9 input=5c0bd5b9079a0cce]*/
{
Py_RETURN_NONE;
}


/*[clinic input]
gh_99233_refcount
Expand Down Expand Up @@ -1046,7 +1159,13 @@ static PyMethodDef tester_methods[] = {
POSONLY_KEYWORDS_OPT_KWONLY_OPT_METHODDEF
POSONLY_OPT_KEYWORDS_OPT_KWONLY_OPT_METHODDEF
KEYWORD_ONLY_PARAMETER_METHODDEF
POSONLY_VARARG_METHODDEF
VARARG_AND_POSONLY_METHODDEF
VARARG_METHODDEF
VARARG_WITH_DEFAULT_METHODDEF
VARARG_WITH_ONLY_DEFAULTS_METHODDEF
GH_32092_OOB_METHODDEF
GH_32092_KW_PASS_METHODDEF
GH_99233_REFCOUNT_METHODDEF
GH_99240_DOUBLE_FREE_METHODDEF
{NULL, NULL}
Expand Down
Loading

0 comments on commit a3dbd4c

Please sign in to comment.