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, b=2), (1, 2, ())) | ||||
|         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): | ||||
|         with self.assertRaises(TypeError): | ||||
|  | @ -3204,6 +3208,37 @@ def test_gh_99240_double_free(self): | |||
|         with self.assertRaisesRegex(TypeError, err): | ||||
|             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): | ||||
|         incorrect_arg = -1  # f1() and f2() accept a single str | ||||
|         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; | ||||
| } | ||||
| 
 | ||||
| /*[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]
 | ||||
| _testclinic.clone_f1 as clone_f1 | ||||
|  | @ -1843,6 +1863,7 @@ static PyMethodDef tester_methods[] = { | |||
|     GH_32092_KW_PASS_METHODDEF | ||||
|     GH_99233_REFCOUNT_METHODDEF | ||||
|     GH_99240_DOUBLE_FREE_METHODDEF | ||||
|     NULL_OR_TUPLE_FOR_VARARGS_METHODDEF | ||||
|     CLONE_F1_METHODDEF | ||||
|     CLONE_F2_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; | ||||
| } | ||||
| 
 | ||||
| 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__, | ||||
| "clone_f1($module, /, path)\n" | ||||
| "--\n" | ||||
|  | @ -3070,4 +3140,4 @@ skip_optional_pos: | |||
| exit: | ||||
|     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 | ||||
|          * so the index is `i + 1`. */ | ||||
|         if (nargs < vararg) { | ||||
|         if (nargs < vararg && i != vararg) { | ||||
|             buf[i] = current_arg; | ||||
|         } | ||||
|         else { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nikita Sobolev
						Nikita Sobolev