python / python/cpython

Potential bug in `type_call` because `kwds` is aliased - passed to both `tp_new` and `tp_init`?

Offen
#120,132 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

type-bug
Vorherrschende Sprache
Python
Sterne
77.2k
Forks
36k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

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

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginnen Sie mit type_call in Objects/typeobject.c und dem Argument-Parsing-Helfer in Python/getargs.c. Verwenden Sie dabei die referenzierten Zeilen und das dokumentierte Verhalten bezüglich geborgter Referenzen. Ermitteln Sie, ob das gemeinsam verwendete kwds-Wörterbuch die Annahmen von PyArg_ParseTupleAndKeywords ungültig machen kann; für den Abschluss sind ein bestätigtes Verhalten, ein festgelegter Ownership-Vertrag und eine Regressionstestabdeckung erforderlich, falls der Fehler reproduziert wird.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
c, python
Bereich
api, backend
Issue-Typ
Bug
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Veraltet
Klarheit
Muss geklärt werden
Anfängerfreundlichkeit
25/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.