summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorJeroen Demeyer <J.Demeyer@UGent.be>2019-08-16 10:41:27 (GMT)
committerMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2019-08-16 10:41:27 (GMT)
commit0567786d26348aa7eaf0ab1b5d038fdabe409d92 (patch)
treefe7c90392253850b8a3111b3177188a8f255dc79 /Objects
parentf3cb68f2e4c3e0c405460f9bb881f5c1db70f535 (diff)
downloadcpython-0567786d26348aa7eaf0ab1b5d038fdabe409d92.zip
cpython-0567786d26348aa7eaf0ab1b5d038fdabe409d92.tar.gz
cpython-0567786d26348aa7eaf0ab1b5d038fdabe409d92.tar.bz2
bpo-37540: vectorcall: keyword names must be strings (GH-14682)
The fact that keyword names are strings is now part of the vectorcall and `METH_FASTCALL` protocols. The biggest concrete change is that `_PyStack_UnpackDict` now checks that and raises `TypeError` if not. CC @markshannon @vstinner https://bugs.python.org/issue37540
Diffstat (limited to 'Objects')
-rw-r--r--Objects/call.c25
1 files changed, 19 insertions, 6 deletions
diff --git a/Objects/call.c b/Objects/call.c
index 7d91789..8a60b3e 100644
--- a/Objects/call.c
+++ b/Objects/call.c
@@ -322,8 +322,7 @@ _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,
assert(nargs >= 0);
assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
assert((nargs == 0 && nkwargs == 0) || stack != NULL);
- /* kwnames must only contains str strings, no subclass, and all keys must
- be unique */
+ /* kwnames must only contain strings and all keys must be unique */
if (co->co_kwonlyargcount == 0 && nkwargs == 0 &&
(co->co_flags & ~PyCF_MASK) == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
@@ -943,12 +942,12 @@ _PyStack_AsDict(PyObject *const *values, PyObject *kwnames)
vector; return NULL with exception set on error. Return the keyword names
tuple in *p_kwnames.
- The newly allocated argument vector supports PY_VECTORCALL_ARGUMENTS_OFFSET.
+ This also checks that all keyword names are strings. If not, a TypeError is
+ raised.
- When done, you must call _PyStack_UnpackDict_Free(stack, nargs, kwnames)
+ The newly allocated argument vector supports PY_VECTORCALL_ARGUMENTS_OFFSET.
- The type of keyword keys is not checked, these checks should be done
- later (ex: _PyArg_ParseStackAndKeywords). */
+ When done, you must call _PyStack_UnpackDict_Free(stack, nargs, kwnames) */
static PyObject *const *
_PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
PyObject **p_kwnames)
@@ -994,7 +993,9 @@ _PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
called in the performance critical hot code. */
Py_ssize_t pos = 0, i = 0;
PyObject *key, *value;
+ unsigned long keys_are_strings = Py_TPFLAGS_UNICODE_SUBCLASS;
while (PyDict_Next(kwargs, &pos, &key, &value)) {
+ keys_are_strings &= Py_TYPE(key)->tp_flags;
Py_INCREF(key);
Py_INCREF(value);
PyTuple_SET_ITEM(kwnames, i, key);
@@ -1002,6 +1003,18 @@ _PyStack_UnpackDict(PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs,
i++;
}
+ /* keys_are_strings has the value Py_TPFLAGS_UNICODE_SUBCLASS if that
+ * flag is set for all keys. Otherwise, keys_are_strings equals 0.
+ * We do this check once at the end instead of inside the loop above
+ * because it simplifies the deallocation in the failing case.
+ * It happens to also make the loop above slightly more efficient. */
+ if (!keys_are_strings) {
+ PyErr_SetString(PyExc_TypeError,
+ "keywords must be strings");
+ _PyStack_UnpackDict_Free(stack, nargs, kwnames);
+ return NULL;
+ }
+
*p_kwnames = kwnames;
return stack;
}