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
Pick-to: 6.6 6.5 6.2 5.15
Change-Id: If77b906c94cb4b9fa91bfad84fe63bc8d9103b0a
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2023-07-05 08:36:06 +02:00
parent bbb71e7e80
commit b933a5668c

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)