Potential bug in `type_call` because `kwds` is aliased - passed to both `tp_new` and `tp_init`?
Nessuno ha ancora preso questa issue.
- Lingua principale
- Python
- Stelle
- 77.2k
- Fork
- 35.9k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
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
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia con type_call in Objects/typeobject.c e con l’helper per l’analisi degli argomenti in Python/getargs.c, utilizzando le righe indicate e il comportamento documentato dei riferimenti presi in prestito. Determina se il dizionario kwds condiviso può invalidare le ipotesi di PyArg_ParseTupleAndKeywords; per considerare il lavoro completato sono necessari un comportamento confermato, un contratto di ownership deciso e una copertura di regressione se il bug viene riprodotto.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- c, python
- Ambito
- api, backend
- Tipo di issue
- Bug
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Stato di attività
- Ferma
- Chiarezza
- Da chiarire
- Idoneità per principianti
- 25/100