Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a3dbd4c

Browse filesBrowse files
authored
[3.11] gh-64490: Fix bugs in argument clinic varargs processing (GH-32092) (#100368)
(cherry picked from commit 0da7283)
1 parent 18b43cf commit a3dbd4c
Copy full SHA for a3dbd4c

File tree

Expand file treeCollapse file tree

7 files changed

+449
-11
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+449
-11
lines changed

‎Lib/test/clinic.test

Copy file name to clipboardExpand all lines: Lib/test/clinic.test
+3-4Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3368,7 +3368,6 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
33683368
static const char * const _keywords[] = {"a", NULL};
33693369
static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg", 0};
33703370
PyObject *argsbuf[2];
3371-
Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
33723371
PyObject *a;
33733372
PyObject *__clinic_args = NULL;
33743373

@@ -3387,7 +3386,7 @@ exit:
33873386

33883387
static PyObject *
33893388
test_vararg_impl(PyObject *module, PyObject *a, PyObject *args)
3390-
/*[clinic end generated code: output=a2baf8c1fade41d2 input=81d33815ad1bae6e]*/
3389+
/*[clinic end generated code: output=ce9334333757f6ea input=81d33815ad1bae6e]*/
33913390

33923391
/*[clinic input]
33933392
test_vararg_with_default
@@ -3418,7 +3417,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar
34183417
static const char * const _keywords[] = {"a", "b", NULL};
34193418
static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg_with_default", 0};
34203419
PyObject *argsbuf[3];
3421-
Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
3420+
Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
34223421
PyObject *a;
34233422
PyObject *__clinic_args = NULL;
34243423
int b = 0;
@@ -3447,7 +3446,7 @@ exit:
34473446
static PyObject *
34483447
test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args,
34493448
int b)
3450-
/*[clinic end generated code: output=3821d282c29f8616 input=6e110b54acd9b22d]*/
3449+
/*[clinic end generated code: output=32fb19dd6bcf9185 input=6e110b54acd9b22d]*/
34513450

34523451
/*[clinic input]
34533452
test_vararg_with_only_defaults

‎Lib/test/test_clinic.py

Copy file name to clipboardExpand all lines: Lib/test/test_clinic.py
+44Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,15 @@ def test_parameters_not_permitted_after_slash_for_now(self):
730730
x: int
731731
""")
732732

733+
def test_parameters_no_more_than_one_vararg(self):
734+
s = self.parse_function_should_fail("""
735+
module foo
736+
foo.bar
737+
*vararg1: object
738+
*vararg2: object
739+
""")
740+
self.assertEqual(s, "Error on line 0:\nToo many var args\n")
741+
733742
def test_function_not_at_column_0(self):
734743
function = self.parse_function("""
735744
module foo
@@ -1222,13 +1231,47 @@ def test_keyword_only_parameter(self):
12221231
ac_tester.keyword_only_parameter(1)
12231232
self.assertEqual(ac_tester.keyword_only_parameter(a=1), (1,))
12241233

1234+
def test_posonly_vararg(self):
1235+
with self.assertRaises(TypeError):
1236+
ac_tester.posonly_vararg()
1237+
self.assertEqual(ac_tester.posonly_vararg(1, 2), (1, 2, ()))
1238+
self.assertEqual(ac_tester.posonly_vararg(1, b=2), (1, 2, ()))
1239+
self.assertEqual(ac_tester.posonly_vararg(1, 2, 3, 4), (1, 2, (3, 4)))
1240+
12251241
def test_vararg_and_posonly(self):
12261242
with self.assertRaises(TypeError):
12271243
ac_tester.vararg_and_posonly()
12281244
with self.assertRaises(TypeError):
12291245
ac_tester.vararg_and_posonly(1, b=2)
12301246
self.assertEqual(ac_tester.vararg_and_posonly(1, 2, 3, 4), (1, (2, 3, 4)))
12311247

1248+
def test_vararg(self):
1249+
with self.assertRaises(TypeError):
1250+
ac_tester.vararg()
1251+
with self.assertRaises(TypeError):
1252+
ac_tester.vararg(1, b=2)
1253+
self.assertEqual(ac_tester.vararg(1, 2, 3, 4), (1, (2, 3, 4)))
1254+
1255+
def test_vararg_with_default(self):
1256+
with self.assertRaises(TypeError):
1257+
ac_tester.vararg_with_default()
1258+
self.assertEqual(ac_tester.vararg_with_default(1, b=False), (1, (), False))
1259+
self.assertEqual(ac_tester.vararg_with_default(1, 2, 3, 4), (1, (2, 3, 4), False))
1260+
self.assertEqual(ac_tester.vararg_with_default(1, 2, 3, 4, b=True), (1, (2, 3, 4), True))
1261+
1262+
def test_vararg_with_only_defaults(self):
1263+
self.assertEqual(ac_tester.vararg_with_only_defaults(), ((), None))
1264+
self.assertEqual(ac_tester.vararg_with_only_defaults(b=2), ((), 2))
1265+
self.assertEqual(ac_tester.vararg_with_only_defaults(1, b=2), ((1, ), 2))
1266+
self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4), ((1, 2, 3, 4), None))
1267+
self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4, b=5), ((1, 2, 3, 4), 5))
1268+
1269+
def test_gh_32092_oob(self):
1270+
ac_tester.gh_32092_oob(1, 2, 3, 4, kw1=5, kw2=6)
1271+
1272+
def test_gh_32092_kw_pass(self):
1273+
ac_tester.gh_32092_kw_pass(1, 2, 3)
1274+
12321275
def test_gh_99233_refcount(self):
12331276
arg = '*A unique string is not referenced by anywhere else.*'
12341277
arg_refcount_origin = sys.getrefcount(arg)
@@ -1241,5 +1284,6 @@ def test_gh_99240_double_free(self):
12411284
with self.assertRaisesRegex(TypeError, expected_error):
12421285
ac_tester.gh_99240_double_free('a', '\0b')
12431286

1287+
12441288
if __name__ == "__main__":
12451289
unittest.main()
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Argument Clinic varargs bugfixes
2+
3+
* Fix out-of-bounds error in :c:func:`!_PyArg_UnpackKeywordsWithVararg`.
4+
* Fix incorrect check which allowed more than one varargs in clinic.py.
5+
* Fix miscalculation of ``noptargs`` in generated code.
6+
* Do not generate ``noptargs`` when there is a vararg argument and no optional argument.
7+

‎Modules/_testclinic.c

Copy file name to clipboardExpand all lines: Modules/_testclinic.c
+119Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,25 @@ keyword_only_parameter_impl(PyObject *module, PyObject *a)
950950
}
951951

952952

953+
/*[clinic input]
954+
posonly_vararg
955+
956+
a: object
957+
/
958+
b: object
959+
*args: object
960+
961+
[clinic start generated code]*/
962+
963+
static PyObject *
964+
posonly_vararg_impl(PyObject *module, PyObject *a, PyObject *b,
965+
PyObject *args)
966+
/*[clinic end generated code: output=ee6713acda6b954e input=783427fe7ec2b67a]*/
967+
{
968+
return pack_arguments_newref(3, a, b, args);
969+
}
970+
971+
953972
/*[clinic input]
954973
vararg_and_posonly
955974
@@ -967,6 +986,100 @@ vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args)
967986
}
968987

969988

989+
/*[clinic input]
990+
vararg
991+
992+
a: object
993+
*args: object
994+
995+
[clinic start generated code]*/
996+
997+
static PyObject *
998+
vararg_impl(PyObject *module, PyObject *a, PyObject *args)
999+
/*[clinic end generated code: output=91ab7a0efc52dd5e input=02c0f772d05f591e]*/
1000+
{
1001+
return pack_arguments_newref(2, a, args);
1002+
}
1003+
1004+
1005+
/*[clinic input]
1006+
vararg_with_default
1007+
1008+
a: object
1009+
*args: object
1010+
b: bool = False
1011+
1012+
[clinic start generated code]*/
1013+
1014+
static PyObject *
1015+
vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args,
1016+
int b)
1017+
/*[clinic end generated code: output=182c01035958ce92 input=68cafa6a79f89e36]*/
1018+
{
1019+
PyObject *obj_b = b ? Py_True : Py_False;
1020+
return pack_arguments_newref(3, a, args, obj_b);
1021+
}
1022+
1023+
1024+
/*[clinic input]
1025+
vararg_with_only_defaults
1026+
1027+
*args: object
1028+
b: object = None
1029+
1030+
[clinic start generated code]*/
1031+
1032+
static PyObject *
1033+
vararg_with_only_defaults_impl(PyObject *module, PyObject *args, PyObject *b)
1034+
/*[clinic end generated code: output=c06b1826d91f2f7b input=678c069bc67550e1]*/
1035+
{
1036+
return pack_arguments_newref(2, args, b);
1037+
}
1038+
1039+
1040+
1041+
/*[clinic input]
1042+
gh_32092_oob
1043+
1044+
pos1: object
1045+
pos2: object
1046+
*varargs: object
1047+
kw1: object = None
1048+
kw2: object = None
1049+
1050+
Proof-of-concept of GH-32092 OOB bug.
1051+
1052+
[clinic start generated code]*/
1053+
1054+
static PyObject *
1055+
gh_32092_oob_impl(PyObject *module, PyObject *pos1, PyObject *pos2,
1056+
PyObject *varargs, PyObject *kw1, PyObject *kw2)
1057+
/*[clinic end generated code: output=ee259c130054653f input=46d15c881608f8ff]*/
1058+
{
1059+
Py_RETURN_NONE;
1060+
}
1061+
1062+
1063+
/*[clinic input]
1064+
gh_32092_kw_pass
1065+
1066+
pos: object
1067+
*args: object
1068+
kw: object = None
1069+
1070+
Proof-of-concept of GH-32092 keyword args passing bug.
1071+
1072+
[clinic start generated code]*/
1073+
1074+
static PyObject *
1075+
gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, PyObject *args,
1076+
PyObject *kw)
1077+
/*[clinic end generated code: output=4a2bbe4f7c8604e9 input=5c0bd5b9079a0cce]*/
1078+
{
1079+
Py_RETURN_NONE;
1080+
}
1081+
1082+
9701083
/*[clinic input]
9711084
gh_99233_refcount
9721085
@@ -1046,7 +1159,13 @@ static PyMethodDef tester_methods[] = {
10461159
POSONLY_KEYWORDS_OPT_KWONLY_OPT_METHODDEF
10471160
POSONLY_OPT_KEYWORDS_OPT_KWONLY_OPT_METHODDEF
10481161
KEYWORD_ONLY_PARAMETER_METHODDEF
1162+
POSONLY_VARARG_METHODDEF
10491163
VARARG_AND_POSONLY_METHODDEF
1164+
VARARG_METHODDEF
1165+
VARARG_WITH_DEFAULT_METHODDEF
1166+
VARARG_WITH_ONLY_DEFAULTS_METHODDEF
1167+
GH_32092_OOB_METHODDEF
1168+
GH_32092_KW_PASS_METHODDEF
10501169
GH_99233_REFCOUNT_METHODDEF
10511170
GH_99240_DOUBLE_FREE_METHODDEF
10521171
{NULL, NULL}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.