Acquire sk_dk_lock during spa_keystore_lookup_key

As seen in #11490, dck_holds may be incremented (acquired) after the
check in dsl_crypto_key_rele, but before the completion of
dsl_crypto_key_free.  All other published accesses to dsl_crypto_key->
dck_holds (i.e., spa_keystore_dsl_key_hold_impl) are protected by its
parent spa->sk_dk_lock.

We avoid the race by also aquiring sk_dk_lock during during
spa_keystore_lookup_key's critical section.

Signed-off-by: Antonio Russo <aerusso@aerusso.net>
This commit is contained in:
Antonio Russo 2021-03-06 03:18:46 -07:00
parent e7a06356c1
commit 95e65c9b9f
No known key found for this signature in database
GPG Key ID: CC324E5538D2EE86
1 changed files with 3 additions and 0 deletions

View File

@ -1095,6 +1095,7 @@ spa_keystore_lookup_key(spa_t *spa, uint64_t dsobj, void *tag,
/* init the search key mapping */ /* init the search key mapping */
search_km.km_dsobj = dsobj; search_km.km_dsobj = dsobj;
rw_enter(&spa->spa_keystore.sk_dk_lock, RW_READER);
rw_enter(&spa->spa_keystore.sk_km_lock, RW_READER); rw_enter(&spa->spa_keystore.sk_km_lock, RW_READER);
/* remove the mapping from the tree */ /* remove the mapping from the tree */
@ -1109,6 +1110,7 @@ spa_keystore_lookup_key(spa_t *spa, uint64_t dsobj, void *tag,
zfs_refcount_add(&found_km->km_key->dck_holds, tag); zfs_refcount_add(&found_km->km_key->dck_holds, tag);
rw_exit(&spa->spa_keystore.sk_km_lock); rw_exit(&spa->spa_keystore.sk_km_lock);
rw_exit(&spa->spa_keystore.sk_dk_lock);
if (dck_out != NULL) if (dck_out != NULL)
*dck_out = found_km->km_key; *dck_out = found_km->km_key;
@ -1116,6 +1118,7 @@ spa_keystore_lookup_key(spa_t *spa, uint64_t dsobj, void *tag,
error_unlock: error_unlock:
rw_exit(&spa->spa_keystore.sk_km_lock); rw_exit(&spa->spa_keystore.sk_km_lock);
rw_exit(&spa->spa_keystore.sk_dk_lock);
if (dck_out != NULL) if (dck_out != NULL)
*dck_out = NULL; *dck_out = NULL;