Fix non-atomic read-modify-write on entry->srtt in adjustsrtt()

The SRTT update loaded the old value, computed a new one, and stored it
back as separate operations.  Two concurrent callers could each read the
same old value and one update would be silently lost.

Use a CAS loop for the read-modify-write on entry->srtt.  For the aging
path, also CAS on entry->lastage to prevent multiple threads from aging
the same entry in the same second.

(cherry picked from commit 4d15494b94)
This commit is contained in:
Ondřej Surý 2026-03-19 04:17:45 +01:00 committed by Ondřej Surý (GitLab job 7061058)
parent be7b811fff
commit fb8a9e73cc

View file

@ -3029,22 +3029,35 @@ static void
adjustsrtt(dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor,
isc_stdtime_t now) {
unsigned int new_srtt;
unsigned int old_srtt;
if (factor == DNS_ADB_RTTADJAGE) {
if (atomic_load(&addr->entry->lastage) != now) {
new_srtt = (uint64_t)atomic_load(&addr->entry->srtt) *
98 / 100;
atomic_store(&addr->entry->lastage, now);
atomic_store(&addr->entry->srtt, new_srtt);
addr->srtt = new_srtt;
isc_stdtime_t lastage =
atomic_load_acquire(&addr->entry->lastage);
/* prevent double aging */
if (lastage == now ||
!atomic_compare_exchange_strong_acq_rel(
&addr->entry->lastage, &lastage, now))
{
return;
}
} else {
new_srtt = ((uint64_t)atomic_load(&addr->entry->srtt) / 10 *
factor) +
((uint64_t)rtt / 10 * (10 - factor));
atomic_store(&addr->entry->srtt, new_srtt);
addr->srtt = new_srtt;
}
/*
* Correct CAS aging...
*/
old_srtt = atomic_load_acquire(&addr->entry->srtt);
do {
if (factor == DNS_ADB_RTTADJAGE) {
new_srtt = (uint64_t)old_srtt * 98 / 100;
} else {
new_srtt = ((uint64_t)old_srtt / 10 * factor) +
((uint64_t)rtt / 10 * (10 - factor));
}
} while (!atomic_compare_exchange_weak_acq_rel(&addr->entry->srtt,
&old_srtt, new_srtt));
addr->srtt = new_srtt;
}
void