Python 3.13 refcounting related memory corruption

Phil Thompson phil at riverbankcomputing.com
Tue May 28 16:58:11 BST 2024


On 28/05/2024 13:34, Florian Bruhin wrote:
> Hey,
> 
> When running the qutebrowser testsuite with Python 3.13, I get a
> segfault pointing to a memory corruption inside Python - including 
> funny
> exceptions such as:
> 
>     TypeError: replace() argument 2 must be str, not +
> 
> when doing
> 
>     value = value.replace('default_family', self.default_family)
> 
> and/or assertions about PyUnicode_KIND returning an invalid value.
> 
> I've reported it here:
> https://github.com/python/cpython/issues/119462
> 
> and bisected it to a change in CPython, "Limit the number of versions
> that a single class can use.":
> https://github.com/python/cpython/pull/114900
> 
> Unfortunately I've not been able to extract a minimal example by any
> means. The best I could get is getting it down to running 11 test files
> in qutebrowser, with almost 2000 test cases. If I remove any of those
> files, the bug doesn't trigger anymore.
> 
> I've now run it through valgrind/ASan, which report that a value
> allocated by PyQt:
> 
>     #0 0x5f1017de70a9 in malloc
> (/home/florian/proj/cpython/python+0x32f0a9) (BuildId:
> 472839294c0413d2f8beb5aa8c1f9ae1822dc88b)
>     #1 0x5f10184cd6c8 in tracemalloc_alloc
> /home/florian/proj/cpython/Python/tracemalloc.c:536:15
>     #2 0x5f10184cdc4d in tracemalloc_alloc_gil
> /home/florian/proj/cpython/Python/tracemalloc.c:640:11
>     #3 0x5f1018192780 in PyUnicode_New
> /home/florian/proj/cpython/Objects/unicodeobject.c:1238:24
>     #4 0x744327ffacf2 in sip_api_unicode_new
> /tmp/pip-install-y87d3n5v/pyqt6-sip_626af1e47840447c9f7c098c3c07b101/sip_core.c:11388:16
>     #5 0x744308e15146 in qpycore_PyObject_FromQString(QString const&)
> (/home/florian/proj/qutebrowser/git/.venv-py313dev/lib/python3.13/site-packages/PyQt6/QtCore.abi3.so+0x215146)
> (BuildId: 74342f967c05e7e19c5fd152ab11397679a17e61)
> 
> gets freed by Python:
> 
>     #0 0x5f1017de6072 in free.part.0 asan_malloc_linux.cpp.o
>     #1 0x5f10184c84d7 in tracemalloc_free
> /home/florian/proj/cpython/Python/tracemalloc.c:614:5
>     #2 0x5f10180e82c7 in _Py_Dealloc
> /home/florian/proj/cpython/Objects/object.c:2875:5
>     #3 0x5f1018098ac3 in Py_DECREF
> /home/florian/proj/cpython/./Include/object.h:922:9
>     #4 0x5f1018098ac3 in Py_XDECREF
> /home/florian/proj/cpython/./Include/object.h:1030:9
>     #5 0x5f1018098ac3 in insertdict
> /home/florian/proj/cpython/Objects/dictobject.c:1311:5
>     #6 0x5f10180b135a in _PyObjectDict_SetItem
> /home/florian/proj/cpython/Objects/dictobject.c
>     #7 0x5f10180e5490 in _PyObject_GenericSetAttrWithDict
> /home/florian/proj/cpython/Objects/object.c:1730:19
>     #8 0x5f101814f877 in type_setattro
> /home/florian/proj/cpython/Objects/typeobject.c:4981:11
>     #9 0x5f10180e20ac in PyObject_SetAttr
> /home/florian/proj/cpython/Objects/object.c:1306:15
> 
> but then accessed by Python again:
> 
>     #0 0x5f101832396c in Py_INCREF
> /home/florian/proj/cpython/./Include/object.h:812:30
>     #1 0x5f101832396c in _Py_NewRef
> /home/florian/proj/cpython/./Include/object.h:1046:5
>     #2 0x5f101832396c in _PyEval_EvalFrameDefault
> /home/florian/proj/cpython/Python/generated_cases.c.h:3795:24
>     #3 0x5f1017fe5a62 in _PyObject_VectorcallTstate
> /home/florian/proj/cpython/./Include/internal/pycore_call.h:168:11
>     #4 0x5f1018159a41 in call_attribute
> /home/florian/proj/cpython/Objects/typeobject.c:8874:15
>     #5 0x5f1018159a41 in _Py_slot_tp_getattr_hook
> /home/florian/proj/cpython/Objects/typeobject.c:8924:19
>     #6 0x5f10180e16a7 in PyObject_GetAttr
> /home/florian/proj/cpython/Objects/object.c:1153:18
>     #7 0x5f10182cdb21 in builtin_getattr
> /home/florian/proj/cpython/Python/bltinmodule.c:1179:18
>     #8 0x5f1018303e87 in _PyEval_EvalFrameDefault
> /home/florian/proj/cpython/Python/generated_cases.c.h:1072:19
>     #9 0x5f101803861c in _PyEval_EvalFrame
> /home/florian/proj/cpython/./Include/internal/pycore_ceval.h:115:16
>     #10 0x5f101803861c in gen_send_ex2
> /home/florian/proj/cpython/Objects/genobject.c:228:24
>     #11 0x5f101803419a in gen_iternext
> /home/florian/proj/cpython/Objects/genobject.c:586:9
>     #12 0x5f1018061d35 in list_extend_iter
> /home/florian/proj/cpython/Objects/listobject.c:985:26
>     #13 0x5f1018061d35 in list_extend
> /home/florian/proj/cpython/Objects/listobject.c:1042:16
>     #14 0x5f1018061628 in _PyList_Extend
> /home/florian/proj/cpython/Objects/listobject.c:1050:9
>     #15 0x5f1017f9a856 in PySequence_List
> /home/florian/proj/cpython/Objects/abstract.c:2135:10
>     #16 0x5f1017f9aaca in PySequence_Fast
> /home/florian/proj/cpython/Objects/abstract.c:2166:9
>     #17 0x5f10181cb7ce in PyUnicode_Join
> /home/florian/proj/cpython/Objects/unicodeobject.c:9569:12
> 
> I'm assuming this is a CPython bug, given that I have it bisected to
> that commit - but it's unclear to me why/how PyQt is involved here.
> 
> Is this anything where PyQt could possibly be to blame for some sort of
> refcounting issue? Is there a better way to debug those rather than
> ASan?
> 
> Thanks,
> Florian

What was the PyQt API call that created the unicode object? When would 
that be deallocated?

Phil


More information about the PyQt mailing list