2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 05:57:52 +00:00

Lock the adbname and adbentry prior to unlocking hash locks

There was a datarace that could expire a freshly created ADB names and
entries between the return from get_attached_{name,entry} and locking it
again.  Lock the ADB name and ADB entry inside the hash table lock, so
they won't get expired until the full initialization has been complete.
This commit is contained in:
Ondřej Surý 2022-12-13 13:48:55 +01:00
parent b8a915b0ac
commit a27ea1bba0
No known key found for this signature in database
GPG Key ID: 2820F37E873DEA41

View File

@ -341,13 +341,13 @@ free_adbfetch(dns_adb_t *, dns_adbfetch_t **);
static void
purge_stale_names(dns_adb_t *adb, isc_stdtime_t now);
static dns_adbname_t *
get_attached_name(dns_adb_t *, const dns_name_t *, bool start_at_zone,
isc_stdtime_t now);
get_attached_and_locked_name(dns_adb_t *, const dns_name_t *,
bool start_at_zone, isc_stdtime_t now);
static void
purge_stale_entries(dns_adb_t *adb, isc_stdtime_t now);
static dns_adbentry_t *
get_attached_entry(dns_adb_t *adb, isc_stdtime_t now,
const isc_sockaddr_t *addr);
get_attached_and_locked_entry(dns_adb_t *adb, isc_stdtime_t now,
const isc_sockaddr_t *addr);
static void
dump_adb(dns_adb_t *, FILE *, bool debug, isc_stdtime_t);
static void
@ -598,9 +598,8 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset,
}
again:
entry = get_attached_entry(adb, now, &sockaddr);
entry = get_attached_and_locked_entry(adb, now, &sockaddr);
LOCK(&entry->lock);
if (ENTRY_DEAD(entry)) {
UNLOCK(&entry->lock);
dns_adbentry_detach(&entry);
@ -1329,8 +1328,8 @@ free_adbaddrinfo(dns_adb_t *adb, dns_adbaddrinfo_t **ainfo) {
* Search for the name in the hash table.
*/
static dns_adbname_t *
get_attached_name(dns_adb_t *adb, const dns_name_t *name, bool start_at_zone,
isc_stdtime_t now) {
get_attached_and_locked_name(dns_adb_t *adb, const dns_name_t *name,
bool start_at_zone, isc_stdtime_t now) {
isc_result_t result;
dns_adbname_t *adbname = NULL;
uint32_t hashval;
@ -1348,7 +1347,7 @@ get_attached_name(dns_adb_t *adb, const dns_name_t *name, bool start_at_zone,
LOCK(&adb->names_lock);
last_update = adb->names_last_update;
if (now - last_update > ADB_STALE_MARGIN ||
if (last_update + ADB_STALE_MARGIN < now ||
atomic_load_relaxed(&adb->is_overmem))
{
last_update = adb->names_last_update = now;
@ -1362,30 +1361,31 @@ get_attached_name(dns_adb_t *adb, const dns_name_t *name, bool start_at_zone,
case ISC_R_NOTFOUND:
/* Allocate a new name and add it to the hash table. */
adbname = new_adbname(adb, name, start_at_zone);
dns_adbname_ref(adbname);
adbname->last_used = now;
result = isc_hashmap_add(adb->names, &hashval,
&adbname->key.key, adbname->key.size,
adbname);
INSIST(result == ISC_R_SUCCESS);
LOCK(&adbname->lock); /* Must be unlocked by the caller */
adbname->last_used = now;
ISC_LIST_PREPEND(adb->names_lru, adbname, link);
break;
case ISC_R_SUCCESS:
dns_adbname_ref(adbname);
LOCK(&adbname->lock);
LOCK(&adbname->lock); /* Must be unlocked by the caller */
if (adbname->last_used + ADB_STALE_MARGIN <= last_update) {
adbname->last_used = now;
ISC_LIST_UNLINK(adb->names_lru, adbname, link);
ISC_LIST_PREPEND(adb->names_lru, adbname, link);
}
UNLOCK(&adbname->lock);
break;
default:
UNREACHABLE();
}
dns_adbname_ref(adbname);
/*
* The refcount is now 2 and the final detach will happen in
* expire_name() - the unused adbname stored in the hashtable and lru
@ -1400,8 +1400,8 @@ get_attached_name(dns_adb_t *adb, const dns_name_t *name, bool start_at_zone,
* Find the entry in the adb->entries hashtable.
*/
static dns_adbentry_t *
get_attached_entry(dns_adb_t *adb, isc_stdtime_t now,
const isc_sockaddr_t *addr) {
get_attached_and_locked_entry(dns_adb_t *adb, isc_stdtime_t now,
const isc_sockaddr_t *addr) {
isc_result_t result;
dns_adbentry_t *adbentry = NULL;
isc_time_t timenow;
@ -1429,20 +1429,20 @@ get_attached_entry(dns_adb_t *adb, isc_stdtime_t now,
create:
/* Allocate a new entry and add it to the hash table. */
adbentry = new_adbentry(adb, addr);
dns_adbentry_ref(adbentry);
adbentry->last_used = now;
result = isc_hashmap_add(adb->entries, &hashval,
&adbentry->sockaddr,
sizeof(adbentry->sockaddr), adbentry);
INSIST(result == ISC_R_SUCCESS);
LOCK(&adbentry->lock); /* Must be unlocked by the caller */
adbentry->last_used = now;
ISC_LIST_PREPEND(adb->entries_lru, adbentry, link);
break;
}
case ISC_R_SUCCESS:
dns_adbentry_ref(adbentry);
LOCK(&adbentry->lock);
LOCK(&adbentry->lock); /* Must be unlocked by the caller */
if (maybe_expire_entry(adbentry, now)) {
UNLOCK(&adbentry->lock);
dns_adbentry_detach(&adbentry);
@ -1454,12 +1454,13 @@ get_attached_entry(dns_adb_t *adb, isc_stdtime_t now,
ISC_LIST_UNLINK(adb->entries_lru, adbentry, link);
ISC_LIST_PREPEND(adb->entries_lru, adbentry, link);
}
UNLOCK(&adbentry->lock);
break;
default:
UNREACHABLE();
}
dns_adbentry_ref(adbentry);
UNLOCK(&adb->entries_lock);
return (adbentry);
@ -2071,9 +2072,9 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action,
again:
/* Try to see if we know anything about this name at all. */
adbname = get_attached_name(adb, name, FIND_STARTATZONE(find), now);
adbname = get_attached_and_locked_name(adb, name,
FIND_STARTATZONE(find), now);
LOCK(&adbname->lock);
if (NAME_DEAD(adbname)) {
UNLOCK(&adbname->lock);
dns_adbname_detach(&adbname);
@ -3506,11 +3507,9 @@ dns_adb_findaddrinfo(dns_adb_t *adb, const isc_sockaddr_t *sa,
return (ISC_R_SHUTTINGDOWN);
}
entry = get_attached_entry(adb, now, sa);
entry = get_attached_and_locked_entry(adb, now, sa);
INSIST(entry != NULL);
LOCK(&entry->lock);
port = isc_sockaddr_getport(sa);
addr = new_adbaddrinfo(adb, entry, port);
*addrp = addr;