Repository navigation
Conversation
|
@ngoldbaum: Would you mind to review this change? |
|
I asked an AI model to look this over and it spotted an issue (CPython's fallback also has this problem): The PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst));
PyObject *_tmp_old_dst = (*_tmp_dst_ptr);If Here is a standalone reproducer using the exact #include <Python.h>
#include <cstdio>
#include <cstring>
#ifndef _Py_CAST
# define _Py_CAST(type, expr) ((type)(expr))
#endif
#ifndef _PyObject_CAST
# define _PyObject_CAST(obj) _Py_CAST(PyObject *, (obj))
#endif
#define REPRO_XSETREF(dst, src) \
do { \
PyObject **_tmp_dst_ptr = _Py_CAST(PyObject **, &(dst)); \
PyObject *_tmp_old_dst = (*_tmp_dst_ptr); \
PyObject *_tmp_src = _PyObject_CAST(src); \
memcpy(_tmp_dst_ptr, &_tmp_src, sizeof(PyObject *)); \
Py_XDECREF(_tmp_old_dst); \
} while (0)
__attribute__((noinline))
static void
replace(PyTypeObject **a, PyTypeObject **b,
PyTypeObject *first, PyTypeObject *second)
{
*a = first;
REPRO_XSETREF(*b, second);
}
int
main()
{
Py_Initialize();
PyType_Slot slots[] = {{0, NULL}};
PyType_Spec spec = {
"repro.T", sizeof(PyObject), 0, Py_TPFLAGS_DEFAULT, slots
};
PyObject *first = PyType_FromSpec(&spec);
PyObject *second = PyType_FromSpec(&spec);
if (first == NULL || second == NULL) {
PyErr_Print();
return 1;
}
Py_ssize_t before = Py_REFCNT(first);
PyTypeObject *slot = NULL;
// Both pointer arguments alias. The assignment makes first the old value
// that REPRO_XSETREF() must release before storing second.
replace(&slot, &slot,
reinterpret_cast<PyTypeObject *>(Py_NewRef(first)),
reinterpret_cast<PyTypeObject *>(Py_NewRef(second)));
std::printf("first refcount: %zd; expected: %zd\n",
Py_REFCNT(first), before);
return 0;
}On CPython 3.10 with GCC 15.2.0: $ g++ -std=c++11 $(python3.10-config --cflags) \
-DPy_LIMITED_API=0x030a0000 -O2 -fstrict-aliasing \
repro.cpp $(python3.10-config --embed --ldflags) -o repro
$ ./repro
first refcount: 3; expected: 2The reference acquired by With |
This issue is super annoying. What do you recommend to fix the issue? Copy/paste the whole Py_SETREF/XSETREF implementation to use |
|
The AI model's suggestion is two memcpys: I don't think this is a performance concern, the compiler should be able to replace the memcpys with a load or store. |
|
If we change the memcpy() implementation of Py_SETREF(), the fix should happen in Python first. |
|
Agreed, sorry for missing this over there. I didn't get a chance to look this week. |
|
No problem. I will prepare a fix for Python later. |
No description provided.