Skip to content

Commit 0da7283

Browse files
authored
gh-64490: Fix bugs in argument clinic varargs processing (#32092)
1 parent 351842b commit 0da7283

11 files changed

+612
-11
lines changed

Include/internal/pycore_global_objects_fini_generated.h

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

+5
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,9 @@ struct _Py_global_strings {
472472
STRUCT_FOR_ID(keyfile)
473473
STRUCT_FOR_ID(keys)
474474
STRUCT_FOR_ID(kind)
475+
STRUCT_FOR_ID(kw)
476+
STRUCT_FOR_ID(kw1)
477+
STRUCT_FOR_ID(kw2)
475478
STRUCT_FOR_ID(lambda)
476479
STRUCT_FOR_ID(last)
477480
STRUCT_FOR_ID(last_node)
@@ -570,6 +573,8 @@ struct _Py_global_strings {
570573
STRUCT_FOR_ID(pid)
571574
STRUCT_FOR_ID(policy)
572575
STRUCT_FOR_ID(pos)
576+
STRUCT_FOR_ID(pos1)
577+
STRUCT_FOR_ID(pos2)
573578
STRUCT_FOR_ID(print_file_and_line)
574579
STRUCT_FOR_ID(priority)
575580
STRUCT_FOR_ID(progress)

Include/internal/pycore_runtime_init_generated.h

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/clinic.test

+3-4
Original file line numberDiff line numberDiff line change
@@ -3845,7 +3845,6 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
38453845
};
38463846
#undef KWTUPLE
38473847
PyObject *argsbuf[2];
3848-
Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
38493848
PyObject *a;
38503849
PyObject *__clinic_args = NULL;
38513850

@@ -3864,7 +3863,7 @@ exit:
38643863

38653864
static PyObject *
38663865
test_vararg_impl(PyObject *module, PyObject *a, PyObject *args)
3867-
/*[clinic end generated code: output=6661f3ca97d85e8c input=81d33815ad1bae6e]*/
3866+
/*[clinic end generated code: output=880365c61ae205d7 input=81d33815ad1bae6e]*/
38683867

38693868
/*[clinic input]
38703869
test_vararg_with_default
@@ -3918,7 +3917,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar
39183917
};
39193918
#undef KWTUPLE
39203919
PyObject *argsbuf[3];
3921-
Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
3920+
Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
39223921
PyObject *a;
39233922
PyObject *__clinic_args = NULL;
39243923
int b = 0;
@@ -3947,7 +3946,7 @@ exit:
39473946
static PyObject *
39483947
test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args,
39493948
int b)
3950-
/*[clinic end generated code: output=5fe3cfccb1bef781 input=6e110b54acd9b22d]*/
3949+
/*[clinic end generated code: output=291e9a5a09831128 input=6e110b54acd9b22d]*/
39513950

39523951
/*[clinic input]
39533952
test_vararg_with_only_defaults

Lib/test/test_clinic.py

+44
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()
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

+119
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)