]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Address race in dns_tsigkey_find()
authorMark Andrews <marka@isc.org>
Wed, 28 Jun 2023 23:15:38 +0000 (09:15 +1000)
committerMichał Kępień <michal@isc.org>
Fri, 5 Jan 2024 10:28:25 +0000 (11:28 +0100)
Restart the process with a write lock if we discover an expired key
while holding the read lock.

(cherry picked from commit d2ba96488ea30eaeb225c12ea77e71de399b6175)

lib/dns/tsig.c

index e102d093996271f2b473395e667e6da7d7f8acdc..8f4c3b9d1b490480616135c56b583b58276e909d 100644 (file)
@@ -1744,8 +1744,9 @@ isc_result_t
 dns_tsigkey_find(dns_tsigkey_t **tsigkey, const dns_name_t *name,
                 const dns_name_t *algorithm, dns_tsig_keyring_t *ring) {
        dns_tsigkey_t *key;
-       isc_stdtime_t now;
        isc_result_t result;
+       isc_rwlocktype_t locktype = isc_rwlocktype_read;
+       isc_stdtime_t now;
 
        REQUIRE(tsigkey != NULL);
        REQUIRE(*tsigkey == NULL);
@@ -1757,25 +1758,30 @@ dns_tsigkey_find(dns_tsigkey_t **tsigkey, const dns_name_t *name,
        RWUNLOCK(&ring->lock, isc_rwlocktype_write);
 
        isc_stdtime_get(&now);
-       RWLOCK(&ring->lock, isc_rwlocktype_read);
+
+again:
+       RWLOCK(&ring->lock, locktype);
        key = NULL;
        result = dns_rbt_findname(ring->keys, name, 0, NULL, (void *)&key);
        if (result == DNS_R_PARTIALMATCH || result == ISC_R_NOTFOUND) {
-               RWUNLOCK(&ring->lock, isc_rwlocktype_read);
+               RWUNLOCK(&ring->lock, locktype);
                return (ISC_R_NOTFOUND);
        }
        if (algorithm != NULL && !dns_name_equal(key->algorithm, algorithm)) {
-               RWUNLOCK(&ring->lock, isc_rwlocktype_read);
+               RWUNLOCK(&ring->lock, locktype);
                return (ISC_R_NOTFOUND);
        }
        if (key->inception != key->expire && isc_serial_lt(key->expire, now)) {
                /*
                 * The key has expired.
                 */
-               RWUNLOCK(&ring->lock, isc_rwlocktype_read);
-               RWLOCK(&ring->lock, isc_rwlocktype_write);
+               if (locktype == isc_rwlocktype_read) {
+                       RWUNLOCK(&ring->lock, locktype);
+                       locktype = isc_rwlocktype_write;
+                       goto again;
+               }
                remove_fromring(key);
-               RWUNLOCK(&ring->lock, isc_rwlocktype_write);
+               RWUNLOCK(&ring->lock, locktype);
                return (ISC_R_NOTFOUND);
        }
 #if 0
@@ -1790,7 +1796,7 @@ dns_tsigkey_find(dns_tsigkey_t **tsigkey, const dns_name_t *name,
        }
 #endif /* if 0 */
        isc_refcount_increment(&key->refs);
-       RWUNLOCK(&ring->lock, isc_rwlocktype_read);
+       RWUNLOCK(&ring->lock, locktype);
        adjust_lru(key);
        *tsigkey = key;
        return (ISC_R_SUCCESS);