[Development] HEADS UP: do not pass const objects to QObject::disconnect(const QMetaObject::Connection&)!

Giuseppe D'Angelo giuseppe.dangelo at kdab.com
Fri Mar 27 02:16:26 CET 2026


Hi,

Il 26/03/26 19:41, Marc Mutz via Development ha scritto:
> Hi,
> 
> We Qt developers cannot figure out how to proceed from here, so let me 
> give you a heads-up here, so you can take matters into your own hands.
> 
> TL;DR: never pass Connection objects originally declared const to 
> QObject::disconnect(Connection).
> 
> The function const_cast<>s away the const of the argument to reset the 
> d_ptr to nullptr. It doesn't matter why, suffice to say that it's not 
> trivial to fix either way on the Qt side.

To be honest, I don't understand the alarmism of this email. There's a 
bug in Qt; and we fix bugs all the time. The impact of this particular 
bug is between miniscule and non-existent.


And I disagree, the bug *is* completely trivial to fix: we can make the 
affected member as `mutable`. The only reason the patch wasn't +2 
already on Gerrit was for the commit message that talked about impending 
deprecations (when there's strong disagreement around it).


If we follow what I consider an extremely speculative line of reasoning, 
and conclude that the bug might be manifesting itself in inline code, 
then there's no solution: people have to update Qt _and_ recompile, 
which is always the case when there's a bug in inline code.

If we reject that line of reasoning (and I do, see below), then it's 
sufficient that people update Qt (UB happens in out-of-line code) and 
they'll get the fix.

In any case the "avoid passing const objects to disconnect()" is there 
as a workaround for those who can recompile their code, but can't 
upgrade Qt. Maybe it can become a gated Clazy check.
But I doubt anyone will listen; their code works completely fine today 
as their compiler doesn't do anything wrong, and they'll never upgrade 
their toolchain without also upgrading Qt.


I don't get why we disagree on such a simple fix that works, can be 100% 
safely cherrypicked in all the open branches, and completely preserves 
behavior (incl. no soft-leaks); and instead we are now researching 
extremely more invasive solutions, including possible deprecations of a 
15-20 years old API for a purely theoretical problem, deprecation which 
requires fixes in all our repositories and will just annoy the heck out 
of our users.


--


Now, the gritty details:

The bug is: there's UB happening in an out of line function. We 
const_cast away an object and mutate it; if the object was originally 
marked as const, that's formally UB.

As many people associate "UB" with "segfault", I need to clarify: NO, 
the program does not crash; it does not write into arbitrary memory / 
readonly memory; it does not trigger sanitizers or anything of the sorts.

In fact, programs work just fine, as demonstrated by the fact that this 
issue is some ~14 years old and we noticed only now (and not because we 
got bug reports: we detected the issue by reading the sources).


I'll state it again:

* the UB happens in out of line code (inside QObject::disconnect(const 
QMetaObject::Connection &)).

* the declaration of a QMetaObject::Connection object as `const` happens 
in client code;

* therefore, compilers do NOT normally see both the const_cast mutating 
the object and original object declared as const, detect UB, and do evil 
things based on the presence of UB. LTO or similar technology would be 
necessary for the compiler to even be able to do this kind of reasoning. 
This firewalling alone reduces the possibility of things going horribly 
wrong to infinitesimally small quantities.

* but even without firewalling, no optimizing compiler we support today 
detects or exploits UBs in const_cast; even when they're "blatant". If 
the Big Three don't optimize on this I reject the notion that "some 
other compiler out there might do it". "Some other compiler we don't 
know about (and therefore DO NOT support)" isn't a starting point.

* I also reject the notion of "what about compilers of tomorrow". People 
who upgrade compilers also upgrade Qt.

* Finally, what *might* be happening (which makes this an issue in 
"inline code", see the discussion above) is that a compiler doesn't 
reload QMetaObject::Connection's data members because the object was 
originally declared `const`.

A sequence like:

   const QMetaObject::Connection c = QObject::connect(~~~);
   assert(c);     // #1
   disconnect(c); // const_cast in here
   assert(c);     // #2

in #1 and #2 touches an inline code path (operator bool()) which loads 
c.d_ptr. An optimizing compiler might read c.d_ptr on #1, and not 
re-read it on #2 (that is, re-use the value read on #1), despite the 
fact that disconnect() call in principle has changed it.

And again major compiler actually does this today:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86318
https://github.com/llvm/llvm-project/issues/160441



So, all in all: this a problem that has zero impact, and we're creating 
a  meteor-size blast radius (deprecation, users annoyed, code to be 
ported, documentation, best practices) ... for what gain exactly?


> In anticipation of a deprecation of the const overload. 

I still reject this idea. It *works* but it's just not worth the hassle.


My 2 c,
-- 
Giuseppe D'Angelo | giuseppe.dangelo at kdab.com | Senior Software Engineer
KDAB (France) S.A.S., a KDAB Group company
Tel. France +33 (0)4 90 84 08 53, http://www.kdab.com
KDAB - Trusted Software Excellence
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 4850 bytes
Desc: Firma crittografica S/MIME
URL: <http://lists.qt-project.org/pipermail/development/attachments/20260327/022c3a01/attachment.bin>


More information about the Development mailing list