Potential bug in `type_call` because `kwds` is aliased - passed to both `tp_new` and `tp_init`?
Ninguém assumiu esta issue ainda.
- Linguagem predominante
- Python
- Estrelas
- 77.2k
- Forks
- 36k
- Métricas de merge de PRs
- Métricas de PR pendentes
Descrição
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
Guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Direção de pesquisa
Comece com type_call em Objects/typeobject.c e o auxiliar de análise de argumentos em Python/getargs.c, usando as linhas referenciadas e o comportamento documentado de referências emprestadas. Determine se o dicionário kwds compartilhado pode invalidar as suposições de PyArg_ParseTupleAndKeywords; para considerar o trabalho concluído, é necessário ter um comportamento confirmado, um contrato de ownership definido e cobertura de regressão se o bug for reproduzido.
Escrita pelo modelo de indexação a partir do texto da issue.
Avaliação
- Stack de tecnologia
- c, python
- Domínio
- api, backend
- Tipo de issue
- Bug
- Dificuldade
- 5/5
- Tempo estimado
- Mais de uma semana
- Status de atividade
- Estagnada
- Clareza
- Precisa de esclarecimento
- Facilidade para iniciantes
- 25/100