mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 21:21:22 +00:00 
			
		
		
		
	gh-110864: Fix _PyArg_UnpackKeywordsWithVararg overwriting vararg with NULL (#110868)
This commit is contained in:
		
							parent
							
								
									db656aebc6
								
							
						
					
					
						commit
						c2192a2bee
					
				
					 5 changed files with 130 additions and 2 deletions
				
			
		|  | @ -3154,6 +3154,10 @@ def test_posonly_vararg(self): | ||||||
|         self.assertEqual(ac_tester.posonly_vararg(1, 2), (1, 2, ())) |         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, b=2), (1, 2, ())) | ||||||
|         self.assertEqual(ac_tester.posonly_vararg(1, 2, 3, 4), (1, 2, (3, 4))) |         self.assertEqual(ac_tester.posonly_vararg(1, 2, 3, 4), (1, 2, (3, 4))) | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             ac_tester.posonly_vararg(b=4) | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             ac_tester.posonly_vararg(1, 2, 3, b=4) | ||||||
| 
 | 
 | ||||||
|     def test_vararg_and_posonly(self): |     def test_vararg_and_posonly(self): | ||||||
|         with self.assertRaises(TypeError): |         with self.assertRaises(TypeError): | ||||||
|  | @ -3204,6 +3208,37 @@ def test_gh_99240_double_free(self): | ||||||
|         with self.assertRaisesRegex(TypeError, err): |         with self.assertRaisesRegex(TypeError, err): | ||||||
|             ac_tester.gh_99240_double_free('a', '\0b') |             ac_tester.gh_99240_double_free('a', '\0b') | ||||||
| 
 | 
 | ||||||
|  |     def test_null_or_tuple_for_varargs(self): | ||||||
|  |         # All of these should not crash: | ||||||
|  |         valid_args_for_test = [ | ||||||
|  |             (('a',), {}, | ||||||
|  |              ('a', (), False)), | ||||||
|  |             (('a', 1, 2, 3), {'covariant': True}, | ||||||
|  |              ('a', (1, 2, 3), True)), | ||||||
|  |             ((), {'name': 'a'}, | ||||||
|  |              ('a', (), False)), | ||||||
|  |             ((), {'name': 'a', 'covariant': True}, | ||||||
|  |              ('a', (), True)), | ||||||
|  |             ((), {'covariant': True, 'name': 'a'}, | ||||||
|  |              ('a', (), True)), | ||||||
|  |         ] | ||||||
|  |         for args, kwargs, expected in valid_args_for_test: | ||||||
|  |             with self.subTest(args=args, kwargs=kwargs): | ||||||
|  |                 self.assertEqual( | ||||||
|  |                     ac_tester.null_or_tuple_for_varargs(*args, **kwargs), | ||||||
|  |                     expected, | ||||||
|  |                 ) | ||||||
|  | 
 | ||||||
|  |     def test_null_or_tuple_for_varargs_error(self): | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             ac_tester.null_or_tuple_for_varargs(covariant=True) | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             ac_tester.null_or_tuple_for_varargs(1, name='a') | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             ac_tester.null_or_tuple_for_varargs(1, 2, 3, name='a', covariant=True) | ||||||
|  |         with self.assertRaises(TypeError): | ||||||
|  |             ac_tester.null_or_tuple_for_varargs(1, 2, 3, covariant=True, name='a') | ||||||
|  | 
 | ||||||
|     def test_cloned_func_exception_message(self): |     def test_cloned_func_exception_message(self): | ||||||
|         incorrect_arg = -1  # f1() and f2() accept a single str |         incorrect_arg = -1  # f1() and f2() accept a single str | ||||||
|         with self.assertRaisesRegex(TypeError, "clone_f1"): |         with self.assertRaisesRegex(TypeError, "clone_f1"): | ||||||
|  |  | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | Fix argument parsing by ``_PyArg_UnpackKeywordsWithVararg`` for functions | ||||||
|  | defining pos-or-keyword, vararg, and kw-only parameters. | ||||||
|  | @ -1128,6 +1128,26 @@ gh_99240_double_free_impl(PyObject *module, char *a, char *b) | ||||||
|     Py_RETURN_NONE; |     Py_RETURN_NONE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /*[clinic input]
 | ||||||
|  | null_or_tuple_for_varargs | ||||||
|  | 
 | ||||||
|  |     name: object | ||||||
|  |     *constraints: object | ||||||
|  |     covariant: bool = False | ||||||
|  | 
 | ||||||
|  | See https://github.com/python/cpython/issues/110864
 | ||||||
|  | [clinic start generated code]*/ | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | null_or_tuple_for_varargs_impl(PyObject *module, PyObject *name, | ||||||
|  |                                PyObject *constraints, int covariant) | ||||||
|  | /*[clinic end generated code: output=a785b35421358983 input=c9bce186637956b3]*/ | ||||||
|  | { | ||||||
|  |     assert(name != NULL); | ||||||
|  |     assert(constraints != NULL); | ||||||
|  |     PyObject *c = covariant ? Py_True : Py_False; | ||||||
|  |     return pack_arguments_newref(3, name, constraints, c); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| /*[clinic input]
 | /*[clinic input]
 | ||||||
| _testclinic.clone_f1 as clone_f1 | _testclinic.clone_f1 as clone_f1 | ||||||
|  | @ -1843,6 +1863,7 @@ static PyMethodDef tester_methods[] = { | ||||||
|     GH_32092_KW_PASS_METHODDEF |     GH_32092_KW_PASS_METHODDEF | ||||||
|     GH_99233_REFCOUNT_METHODDEF |     GH_99233_REFCOUNT_METHODDEF | ||||||
|     GH_99240_DOUBLE_FREE_METHODDEF |     GH_99240_DOUBLE_FREE_METHODDEF | ||||||
|  |     NULL_OR_TUPLE_FOR_VARARGS_METHODDEF | ||||||
|     CLONE_F1_METHODDEF |     CLONE_F1_METHODDEF | ||||||
|     CLONE_F2_METHODDEF |     CLONE_F2_METHODDEF | ||||||
|     CLONE_WITH_CONV_F1_METHODDEF |     CLONE_WITH_CONV_F1_METHODDEF | ||||||
|  |  | ||||||
							
								
								
									
										72
									
								
								Modules/clinic/_testclinic.c.h
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										72
									
								
								Modules/clinic/_testclinic.c.h
									
										
									
										generated
									
									
									
								
							|  | @ -2813,6 +2813,76 @@ exit: | ||||||
|     return return_value; |     return return_value; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | PyDoc_STRVAR(null_or_tuple_for_varargs__doc__, | ||||||
|  | "null_or_tuple_for_varargs($module, /, name, *constraints,\n" | ||||||
|  | "                          covariant=False)\n" | ||||||
|  | "--\n" | ||||||
|  | "\n" | ||||||
|  | "See https://github.com/python/cpython/issues/110864"); | ||||||
|  | 
 | ||||||
|  | #define NULL_OR_TUPLE_FOR_VARARGS_METHODDEF    \ | ||||||
|  |     {"null_or_tuple_for_varargs", _PyCFunction_CAST(null_or_tuple_for_varargs), METH_FASTCALL|METH_KEYWORDS, null_or_tuple_for_varargs__doc__}, | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | null_or_tuple_for_varargs_impl(PyObject *module, PyObject *name, | ||||||
|  |                                PyObject *constraints, int covariant); | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | null_or_tuple_for_varargs(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) | ||||||
|  | { | ||||||
|  |     PyObject *return_value = NULL; | ||||||
|  |     #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) | ||||||
|  | 
 | ||||||
|  |     #define NUM_KEYWORDS 2 | ||||||
|  |     static struct { | ||||||
|  |         PyGC_Head _this_is_not_used; | ||||||
|  |         PyObject_VAR_HEAD | ||||||
|  |         PyObject *ob_item[NUM_KEYWORDS]; | ||||||
|  |     } _kwtuple = { | ||||||
|  |         .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) | ||||||
|  |         .ob_item = { &_Py_ID(name), &_Py_ID(covariant), }, | ||||||
|  |     }; | ||||||
|  |     #undef NUM_KEYWORDS | ||||||
|  |     #define KWTUPLE (&_kwtuple.ob_base.ob_base) | ||||||
|  | 
 | ||||||
|  |     #else  // !Py_BUILD_CORE
 | ||||||
|  |     #  define KWTUPLE NULL | ||||||
|  |     #endif  // !Py_BUILD_CORE
 | ||||||
|  | 
 | ||||||
|  |     static const char * const _keywords[] = {"name", "covariant", NULL}; | ||||||
|  |     static _PyArg_Parser _parser = { | ||||||
|  |         .keywords = _keywords, | ||||||
|  |         .fname = "null_or_tuple_for_varargs", | ||||||
|  |         .kwtuple = KWTUPLE, | ||||||
|  |     }; | ||||||
|  |     #undef KWTUPLE | ||||||
|  |     PyObject *argsbuf[3]; | ||||||
|  |     Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; | ||||||
|  |     PyObject *name; | ||||||
|  |     PyObject *constraints = NULL; | ||||||
|  |     int covariant = 0; | ||||||
|  | 
 | ||||||
|  |     args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf); | ||||||
|  |     if (!args) { | ||||||
|  |         goto exit; | ||||||
|  |     } | ||||||
|  |     name = args[0]; | ||||||
|  |     constraints = args[1]; | ||||||
|  |     if (!noptargs) { | ||||||
|  |         goto skip_optional_kwonly; | ||||||
|  |     } | ||||||
|  |     covariant = PyObject_IsTrue(args[2]); | ||||||
|  |     if (covariant < 0) { | ||||||
|  |         goto exit; | ||||||
|  |     } | ||||||
|  | skip_optional_kwonly: | ||||||
|  |     return_value = null_or_tuple_for_varargs_impl(module, name, constraints, covariant); | ||||||
|  | 
 | ||||||
|  | exit: | ||||||
|  |     Py_XDECREF(constraints); | ||||||
|  |     return return_value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| PyDoc_STRVAR(clone_f1__doc__, | PyDoc_STRVAR(clone_f1__doc__, | ||||||
| "clone_f1($module, /, path)\n" | "clone_f1($module, /, path)\n" | ||||||
| "--\n" | "--\n" | ||||||
|  | @ -3070,4 +3140,4 @@ skip_optional_pos: | ||||||
| exit: | exit: | ||||||
|     return return_value; |     return return_value; | ||||||
| } | } | ||||||
| /*[clinic end generated code: output=c2a69a08ffdfc466 input=a9049054013a1b77]*/ | /*[clinic end generated code: output=aea5f282f24761aa input=a9049054013a1b77]*/ | ||||||
|  |  | ||||||
|  | @ -2522,7 +2522,7 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs, | ||||||
|          * |          * | ||||||
|          * Otherwise, we leave a place at `buf[vararg]` for vararg tuple |          * Otherwise, we leave a place at `buf[vararg]` for vararg tuple | ||||||
|          * so the index is `i + 1`. */ |          * so the index is `i + 1`. */ | ||||||
|         if (nargs < vararg) { |         if (nargs < vararg && i != vararg) { | ||||||
|             buf[i] = current_arg; |             buf[i] = current_arg; | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nikita Sobolev
						Nikita Sobolev