Skip to content

fix(ffi): don't steal Arc in dc_jsonrpc_init#7961

Closed
d2weber wants to merge 1 commit intochatmail:mainfrom
d2weber:fix_double_free
Closed

fix(ffi): don't steal Arc in dc_jsonrpc_init#7961
d2weber wants to merge 1 commit intochatmail:mainfrom
d2weber:fix_double_free

Conversation

@d2weber
Copy link
Collaborator

@d2weber d2weber commented Mar 6, 2026

dc_jsonrpc_init called Arc::from_raw on the account_manager pointer, which took ownership of the caller's refcount. When the local Arc dropped at the end of the function, the refcount was decremented, leaving the C side's pointer with a stolen refcount. This caused a use-after-free race between dc_accounts_unref and dc_jsonrpc_unref at shutdown.

Wrap in ManuallyDrop to prevent the implicit drop, keeping the caller's refcount intact.

Regression introduced in #7662.


We encountered this issue in deltatouch. It manifests as a double free and thus a sporadic SIGABRT when shutting down the app, caused by malloc which complains: "corrupted double-linked list"

For reference, here the backtrace when deltatouch crashes after the double free.
#0 __pthread_kill_implementation (no_tid=0, signo=6, threadid=<optimised out>) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=6, threadid=<optimised out>) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (threadid=<optimised out>, signo=[signo@entry](mailto:signo@entry)=6) at ./nptl/pthread_kill.c:89
#3 0x00007fffea84527e in __GI_raise (sig=[sig@entry](mailto:sig@entry)=6) at ../sysdeps/posix/raise.c:26
#4 0x00007fffea8288ff in __GI_abort () at ./stdlib/abort.c:79
#5 0x00007fffea8297b6 in __libc_message_impl (fmt=[fmt@entry](mailto:fmt@entry)=0x7fffea9ce8d7 "%s\n") at ../sysdeps/posix/libc_fatal.c:134
#6 0x00007fffea8a8ff5 in malloc_printerr (str=[str@entry](mailto:str@entry)=0x7fffea9d1de0 "malloc(): unsorted double linked list corrupted")
    at ./malloc/malloc.c:5775
#7 0x00007fffea8abd7c in _int_malloc (av=[av@entry](mailto:av@entry)=0x7fffeaa03ac0 <main_arena>, bytes=[bytes@entry](mailto:bytes@entry)=16384) at ./malloc/malloc.c:4086
#8 0x00007fffea8ad714 in __GI___libc_malloc (bytes=16384) at ./malloc/malloc.c:3336
#9 0x00007fffeb0e70ea in QArrayData::allocate(unsigned long, unsigned long, unsigned long, QFlags<QArrayData::AllocationOption>) ()
    at /lib/x86_64-linux-gnu/libQt5Core.so.5
#10 0x00007fffeb8b2717 in ??? () at /lib/x86_64-linux-gnu/libQt5Qml.so.5
#11 0x00007fffeb936d1b in QV4::WeakValue::free() () at /lib/x86_64-linux-gnu/libQt5Qml.so.5
#12 0x00007fffeba8a5f3 in QQmlData::destroyed(QObject*) () at /lib/x86_64-linux-gnu/libQt5Qml.so.5
#13 0x00007fffeb310256 in QObject::~QObject() () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#14 0x00007fffeba8d9ef in ??? () at /lib/x86_64-linux-gnu/libQt5Qml.so.5
#15 0x00007fffeb303dfe in QObjectPrivate::deleteChildren() () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#16 0x00007fffeb310601 in QObject::~QObject() () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#17 0x00007fffec86d845 in QQuickItem::~QQuickItem() () at /lib/x86_64-linux-gnu/libQt5Quick.so.5
#18 0x00007fffeb303dfe in QObjectPrivate::deleteChildren() () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#19 0x00007fffeb310601 in QObject::~QObject() () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#20 0x00007fffc62d5579 in ??? () at /lib/x86_64-linux-gnu/libLomiriToolkit.so.5
#21 0x00007ffff7e1b8ec in QQmlDelegateModel::~QQmlDelegateModel() () at /lib/x86_64-linux-gnu/libQt5QmlModels.so.5
#22 0x00007ffff7e1ba95 in QQmlDelegateModel::~QQmlDelegateModel() () at /lib/x86_64-linux-gnu/libQt5QmlModels.so.5
#23 0x00007fffec92fe37 in QQuickItemView::~QQuickItemView() () at /lib/x86_64-linux-gnu/libQt5Quick.so.5
#24 0x00007fffec9fcd21 in ??? () at /lib/x86_64-linux-gnu/libQt5Quick.so.5
#25 0x00007fffeb303dfe in QObjectPrivate::deleteChildren() () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#26 0x00007fffeb310601 in QObject::~QObject() () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#27 0x00007fffc62d66b5 in ??? () at /lib/x86_64-linux-gnu/libLomiriToolkit.so.5
#28 0x00007fffeb30610b in QObject::event(QEvent*) () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#29 0x00007fffeb2d8118 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#30 0x00007fffeb2db94b in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) ()
    at /lib/x86_64-linux-gnu/libQt5Core.so.5
#31 0x00007fffeb2df401 in QCoreApplication::exec() () at /lib/x86_64-linux-gnu/libQt5Core.so.5
#32 0x00005555555597da in main (argc=1, argv=0x7fffffffe1c8) at /deltatouch/src/main.cpp:314
Valgrind reports the double free from `dc_accounts_unref` and `dc_jsonrpc_unref`
==35926== Invalid read of size 8
==35926==    at 0x58DE64E: dc_accounts_unref (in /chatmail-core-2.43.0/libdeltachat.so)
==35926==    by 0x27EFF9B7: DeltaHandler::shutdownTasks() (src/plugins/DeltaHandler/deltahandler.cpp:6859)
==35926==    by 0x27EC02EF: DeltaHandler::qt_metacall(QMetaObject::Call, int, void**) (moc_deltahandler.cpp:0)
==35926==    by 0x10A7B6A9: ??? (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x1094D0DF: ??? (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x109502D9: QV4::QObjectMethod::callInternal(QV4::Value const*, QV4::Value const*, int) const (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x1096DDDB: ??? (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x109715FE: ??? (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x109015A1: QV4::Function::call(QV4::Value const*, QV4::Value const*, int, QV4::ExecutionContext const*) (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x10A972BF: QQmlJavaScriptExpression::evaluate(QV4::CallData*, bool*) (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x10A45860: QQmlBoundSignalExpression::evaluate(void**) (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x10A46ADF: ??? (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==  Address 0x28a94080 is 0 bytes inside a block of size 240 free'd
==35926==    at 0x484988F: free (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==35926==    by 0x5978C76: core::ptr::drop_in_place<yerpc::requests::RpcSession<deltachat_jsonrpc::api::CommandApi>> (in /chatmail-core-2.43.0/libdeltachat.so)
==35926==    by 0x58E7AC9: dc_jsonrpc_unref (in /chatmail-core-2.43.0/libdeltachat.so)
==35926==    by 0x27ECD72F: JsonrpcResponseThread::run() (src/plugins/DeltaHandler/jsonrpcresponsethread.cpp:44)
==35926==    by 0x10F3C673: ??? (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.15.13)
==35926==    by 0x11807AA3: start_thread (pthread_create.c:447)
==35926==    by 0x11894A63: clone (clone.S:100)
==35926==  Block was alloc'd at
==35926==    at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==35926==    by 0x58DDC04: dc_accounts_new (in /chatmail-core-2.43.0/libdeltachat.so)
==35926==    by 0x27ECEEDB: DeltaHandler::DeltaHandler(QObject*) (src/plugins/DeltaHandler/deltahandler.cpp:260)
==35926==    by 0x27EC537A: operator() (src/plugins/DeltaHandler/plugin.cpp:29)
==35926==    by 0x27EC537A: DeltaHandlerPlugin::registerTypes(char const*)::$_0::__invoke(QQmlEngine*, QJSEngine*) (src/plugins/DeltaHandler/plugin.cpp:29)
==35926==    by 0x10A2B66F: QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(QQmlType const&) (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x1092774A: QV4::QQmlContextWrapper::getPropertyAndBase(QV4::QQmlContextWrapper const*, QV4::PropertyKey, QV4::Value const*, bool*, QV4::Value*, QV4::Lookup*) (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x109279EE: QV4::QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(QV4::Lookup*, QV4::ExecutionEngine*, QV4::Value*) (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x1096BF23: ??? (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x109715FE: ??? (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x109015A1: QV4::Function::call(QV4::Value const*, QV4::Value const*, int, QV4::ExecutionContext const*) (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x10A972BF: QQmlJavaScriptExpression::evaluate(QV4::CallData*, bool*) (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926==    by 0x10A9DA46: QQmlBinding::evaluate(bool*) (in /usr/lib/x86_64-linux-gnu/libQt5Qml.so.5.15.13)
==35926== 

dc_jsonrpc_init called Arc::from_raw on the account_manager pointer,
which took ownership of the caller's refcount. When the local Arc
dropped at the end of the function, the refcount was decremented,
leaving the C side's pointer with a stolen refcount. This caused a
use-after-free race between dc_accounts_unref and dc_jsonrpc_unref
at shutdown.

Wrap in ManuallyDrop to prevent the implicit drop, keeping the
caller's refcount intact.

Regression introduced in chatmail#7662.
@d2weber
Copy link
Collaborator Author

d2weber commented Mar 6, 2026

Replaced with #7962

@d2weber d2weber closed this Mar 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant