atomic.cpp: fix race in cond_id_lock()

This commit is contained in:
Nekotekina 2022-05-07 20:36:22 +03:00 committed by Ivan
parent 9a93b150f0
commit 0a5ea859ea
1 changed files with 29 additions and 4 deletions

View File

@ -665,10 +665,17 @@ static void cond_free(u32 cond_id, u32 tls_slot = -1)
static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0) static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0)
{ {
if (cond_id - 1 < u16{umax}) bool did_ref = false;
if (cond_id - 1 >= u16{umax})
{ {
return nullptr;
}
const auto cond = s_cond_list + cond_id; const auto cond = s_cond_list + cond_id;
while (true)
{
const auto [old, ok] = cond->ptr_ref.fetch_op([&](u64& val) const auto [old, ok] = cond->ptr_ref.fetch_op([&](u64& val)
{ {
if (!val || (val & s_ref_mask) == s_ref_mask) if (!val || (val & s_ref_mask) == s_ref_mask)
@ -695,12 +702,23 @@ static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0)
return false; return false;
} }
if (!did_ref)
{
val++; val++;
}
return true; return true;
}); });
if (ok) if (ok)
{ {
// Check other fields again
if (const u32 sync_val = cond->sync; sync_val == 0 || sync_val == 3 || (cond->size && !(mask & cond->mask)))
{
did_ref = true;
continue;
}
return cond; return cond;
} }
@ -708,6 +726,13 @@ static cond_handle* cond_id_lock(u32 cond_id, u128 mask, uptr iptr = 0)
{ {
fmt::throw_exception("Reference count limit (131071) reached in an atomic notifier."); fmt::throw_exception("Reference count limit (131071) reached in an atomic notifier.");
} }
break;
}
if (did_ref)
{
cond_free(cond_id, -1);
} }
return nullptr; return nullptr;