Potential bug in `type_call` because `kwds` is aliased - passed to both `tp_new` and `tp_init`?
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Python
- Star
- 77.2k
- Fork
- 35.9k
- Chỉ số merge pull request
- Chỉ số pull request đang chờ
Mô tả
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
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu với type_call trong Objects/typeobject.c và trình trợ giúp phân tích đối số trong Python/getargs.c, sử dụng các dòng được tham chiếu và hành vi tham chiếu mượn đã được ghi lại. Xác định xem từ điển kwds dùng chung có thể làm mất hiệu lực các giả định của PyArg_ParseTupleAndKeywords hay không; để hoàn tất, cần xác nhận được hành vi, quyết định hợp đồng ownership và có kiểm thử hồi quy nếu tái hiện được lỗi.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- c, python
- Lĩnh vực
- api, backend
- Loại issue
- Lỗi
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức độ hoạt động
- Đình trệ
- Độ rõ ràng
- Cần làm rõ
- Mức phù hợp với người mới
- 25/100