Potential bug in `type_call` because `kwds` is aliased - passed to both `tp_new` and `tp_init`?
Personne n'a encore pris cette issue.
- Langage dominant
- Python
- Étoiles
- 77.2k
- Forks
- 35.9k
- Métriques de merge des PR
- Métriques de PR en attente
Description
Bug report
Bug description:
Hello, I think there may be an extremely contrived bug in type_call. I've tried to do my due diligence of studying the code (I also had a discussion on libera.chat#python-dev), but I apologize in advance if I've still misunderstood it. It's a long(ish) problem to explain; I will try to keep it minimal and explain it in 3 parts.
Suppose I write a C extension that calls PyDict_GetItem twice.
PyObject* x = PyDict_GetItem(dict, key1); // assume this succeeds
PyObject* y = PyDict_GetItem(dict, key2);
This code should be incorrect, since the hashing and comparison of keys may execute arbitrary Python code. If dict has been externally aliased (e.g. stored in a global variable), the lookup of key2 could delete key1 from the dictionary. The correct code needs to call Py_INCREF(x) between the two calls of PyDict_GetItem.
Next, consider PyArg_ParseTupleAndKeywords. This function returns borrowed references, which is documented here. In the helper function for PyArg_ParseTupleAndKeywords (and related functions), the current/recent code gets a strong reference, but calls Py_DECREF after convertitem, before the next iteration of the loop. In the 3.12 code, you can see that the borrowed-reference functions are used, and no Py_DECREF is needed after convertitem.
So PyArg_ParseTupleAndKeywords (effectively) is making multiple calls to PyDict_GetItem and keeping borrowed references. However, this should be (usually) ok because, as I understand, when a function is called with keyword arguments, CPython generally ensures that the dictionary is unique to the callee. In other words, kwargs in vgetargskeywords should not be aliased.
However, in type_call, the same kwds dictionary is directly passed to both the tp_new and tp_init slot. A C extension type may implement tp_new which aliases kwds, and implement tp_init using PyArg_ParseTupleAndKeywords, which is nolonger safe, even though the C extension hasn't broken any contracts (that I'm aware of).
Of course, this is extremely contrived, but I don't think it necessarily involves "malicious" code (by some definition of malicious). One potential "culprit" to point fingers at is to say "the C extension developer shouldn't have wrote code like that (aliasing the keyword arguments)". On the other hand, it seems "innocuous enough" that the developer may, for example, store the keyword arguments in a global dictionary (in particular, in Python land) for debugging purposes.
CPython versions tested on:
3.12, 3.13
Operating systems tested on:
No response
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Commencez par type_call dans Objects/typeobject.c et l’utilitaire d’analyse des arguments dans Python/getargs.c, en vous appuyant sur les lignes référencées et le comportement documenté des références empruntées. Déterminez si le dictionnaire kwds partagé peut invalider les hypothèses de PyArg_ParseTupleAndKeywords ; la tâche ne pourra être considérée comme terminée qu’avec un comportement confirmé, un contrat d’ownership défini et une couverture de régression si le bug est reproduit.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- c, python
- Domaine
- api, backend
- Type d'issue
- Bug
- Difficulté
- 5/5
- Temps estimé
- Plus d'une semaine
- Activité
- À l'abandon
- Clarté
- À clarifier
- Accessibilité débutants
- 25/100