Make sure we don't count down past 0 QT_FATAL_CRITICALS

The old code first checked for == 0, then, if false, executed a
fetchAndAdd(-1), both with relaxed memory ordering. This can lead to
executions that, counter to what the code comment states, can count
down past 0:

    // T1                   T2
    loadRelaxed()                                  // true
                            loadRelaxed()          // true
    fetchAndAddRelaxed(-1)                         // e.g. 1 → 0
                            fetchAndAddRelaxed(-1) // 0 → -1

while fatality is detected exactly once, this execution doesn't stop
at 0 and causes further calls to isFatal() to count down further, with
the (very) remote spectre of underflow past INT_MIN.

Fix by using a CAS loop instead, so each count-down uses only one
step, not two, which therefore can no longer interleave.

Fixes: QTBUG-115062
Change-Id: If77b906c94cb4b9fa91bfad84fe63bc8d9103b0a
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
(cherry picked from commit b933a5668cc5647d26378f8a9a52901d0497585d)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Marc Mutz 2023-07-05 08:36:06 +02:00 committed by Qt Cherry-pick Bot
parent 63428b1f1f
commit c9fe146401

View File

@ -188,7 +188,11 @@ static bool is_fatal_count_down(QAtomicInt &n)
{
// it's fatal if the current value is exactly 1,
// otherwise decrement if it's non-zero
return n.loadRelaxed() && n.fetchAndAddRelaxed(-1) == 1;
int v = n.loadRelaxed();
while (v != 0 && !n.testAndSetRelaxed(v, v - 1, v))
;
return v == 1; // we exited the loop, so either v == 0 or CAS succeeded to set n from v to v-1
}
static bool isFatal(QtMsgType msgType)