Fix blk-crypto keyslot race condition
Subsystems (5)
Eric Biggers
maintainerv3 cover Fix blk-crypto keyslot race condition
This series fixes a race condition in blk-crypto keyslot management and makes some related cleanups. It replaces
"[PATCH] blk-crypto: make blk_crypto_evict_key() always try to evict"
(https://lore.kernel.org/r/[email protected]),
which was a simpler fix but didn't fix the keyslot reference counting to work as expected.
Changed in v3:
- Add a patch that makes blk_crypto_evict_key() return void
- Add a patch that makes blk_insert_cloned_request() pass on the actual error code from blk_crypto_rq_put_keyslot()
- Use gotos in __blk_crypto_evict_key()
Changed in v2:
- Call blk_crypto_rq_put_keyslot() when requests are merged
- Add and use blk_crypto_rq_has_keyslot()
- Add patch "blk-crypto: drop the NULL check from blk_crypto_put_keyslot()"
Eric Biggers (6):
blk-mq: release crypto keyslot before reporting I/O complete
blk-crypto: make blk_crypto_evict_key() return void
blk-crypto: make blk_crypto_evict_key() more robust
blk-crypto: remove blk_crypto_insert_cloned_request()
blk-mq: return actual keyslot error in blk_insert_cloned_request()
blk-crypto: drop the NULL check from blk_crypto_put_keyslot()
Documentation/block/inline-encryption.rst | 3 +- block/blk-crypto-internal.h | 38 ++++++------- block/blk-crypto-profile.c | 60 +++++++++------------ block/blk-crypto.c | 66 +++++++++++++---------- block/blk-merge.c | 2 + block/blk-mq.c | 20 +++++-- drivers/md/dm-table.c | 19 ++----- include/linux/blk-crypto.h | 4 +- 8 files changed, 110 insertions(+), 102 deletions(-)
base-commit: eeac8ede17557680855031c6f305ece2378af326
Signature · 1 line
2.39.2
Eric Biggers
maintainerPATCH v3 1/6 blk-mq: release crypto keyslot before reporting I/O complete
From: Eric Biggers <[email protected]>
Once all I/O using a blk_crypto_key has completed, filesystems can call blk_crypto_evict_key(). However, the block layer currently doesn't call blk_crypto_put_keyslot() until the request is being freed, which happens after upper layers have been told (via bio_endio()) the I/O has completed. This causes a race condition where blk_crypto_evict_key() can see 'slot_refs != 0' without there being an actual bug.
This makes __blk_crypto_evict_key() hit the 'WARN_ON_ONCE(atomic_read(&slot->slot_refs) != 0)' and return without doing anything, eventually causing a use-after-free in blk_crypto_reprogram_all_keys(). (This is a very rare bug and has only been seen when per-file keys are being used with fscrypt.)
There are two options to fix this: either release the keyslot before bio_endio() is called on the request's last bio, or make __blk_crypto_evict_key() ignore slot_refs. Let's go with the first solution, since it preserves the ability to report bugs (via WARN_ON_ONCE) where a key is evicted while still in-use.
--- block/blk-crypto-internal.h | 25 +++++++++++++++++++++---- block/blk-crypto.c | 24 ++++++++++++------------ block/blk-merge.c | 2 ++ block/blk-mq.c | 15 ++++++++++++++- 4 files changed, 49 insertions(+), 17 deletions(-)
block/blk-crypto-internal.h +21 -4
@@ -65,6 +65,11 @@ static inline bool blk_crypto_rq_is_encrypted(struct request *rq)return rq->crypt_ctx; } +static inline bool blk_crypto_rq_has_keyslot (struct request *rq) +{ +return rq->crypt_keyslot; +} +blk_status_t blk_crypto_get_keyslot (struct blk_crypto_profile *profile,const struct blk_crypto_key *key,struct blk_crypto_keyslot **slot_ptr);
@@ -119,6 +124,11 @@ static inline bool blk_crypto_rq_is_encrypted(struct request *rq) return false; } +static inline bool blk_crypto_rq_has_keyslot (struct request *rq) +{ +return false; +} +#endif /* CONFIG_BLK_INLINE_ENCRYPTION */ void __bio_crypt_advance (struct bio *bio,unsigned int bytes);
@@ -153,14 +163,21 @@ static inline bool blk_crypto_bio_prep(struct bio **bio_ptr)return true; } -blk_status_t __blk_crypto_init_request (struct request *rq); -static inline blk_status_t blk_crypto_init_request (struct request *rq) +blk_status_t __blk_crypto_rq_get_keyslot (struct request *rq); +static inline blk_status_t blk_crypto_rq_get_keyslot (struct request *rq) {if (blk_crypto_rq_is_encrypted (rq)) -return __blk_crypto_init_request (rq); +return __blk_crypto_rq_get_keyslot (rq);return BLK_STS_OK ; } +void __blk_crypto_rq_put_keyslot (struct request *rq); +static inline void blk_crypto_rq_put_keyslot (struct request *rq) +{ +if (blk_crypto_rq_has_keyslot (rq)) +__blk_crypto_rq_put_keyslot (rq); +} +void __blk_crypto_free_request (struct request *rq);static inline void blk_crypto_free_request (struct request *rq) {
@@ -199,7 +216,7 @@ static inline blk_status_t blk_crypto_insert_cloned_request(struct request *rq) {if (blk_crypto_rq_is_encrypted (rq)) -return blk_crypto_init_request (rq); +return blk_crypto_rq_get_keyslot (rq);return BLK_STS_OK ; }
block/blk-crypto.c +12 -12
@@ -224,27 +224,27 @@ static bool bio_crypt_check_alignment(struct bio *bio)return true; } -blk_status_t __blk_crypto_init_request (struct request *rq) +blk_status_t __blk_crypto_rq_get_keyslot (struct request *rq) {return blk_crypto_get_keyslot (rq->q->crypto_profile, rq->crypt_ctx->bc_key, &rq->crypt_keyslot); } -/** -* __blk_crypto_free_request - Uninitialize the crypto fields of a request. -* -* @rq: The request whose crypto fields to uninitialize. -* -* Completely uninitializes the crypto fields of a request. If a keyslot has -* been programmed into some inline encryption hardware, that keyslot is -* released. The rq->crypt_ctx is also freed. -*/ -void __blk_crypto_free_request (struct request *rq) +void __blk_crypto_rq_put_keyslot (struct request *rq) {blk_crypto_put_keyslot (rq->crypt_keyslot); + rq->crypt_keyslot =NULL ; +} + +void __blk_crypto_free_request (struct request *rq) +{ +/* The keyslot, if one was needed, should have been released earlier. */ +if (WARN_ON_ONCE (rq->crypt_keyslot)) +__blk_crypto_rq_put_keyslot (rq); +mempool_free (rq->crypt_ctx, bio_crypt_ctx_pool); -blk_crypto_rq_set_defaults (rq); + rq->crypt_ctx =NULL ; }/**
block/blk-merge.c +2 -0
@@ -867,6 +867,8 @@ static struct request *attempt_merge(struct request_queue *q,if (!blk_discard_mergable (req))elv_merge_requests (q, req, next); +blk_crypto_rq_put_keyslot (next); +/* * 'next' is going away, so update stats accordingly */
block/blk-mq.c +14 -1
@@ -840,6 +840,12 @@ static void blk_complete_request(struct request *req) req->q->integrity.profile->complete_fn (req, total_bytes);#endif +/* +* Upper layers may call blk_crypto_evict_key() anytime after the last +* bio_endio(). Therefore, the keyslot must be released before that. +*/ +blk_crypto_rq_put_keyslot (req); +blk_account_io_completion (req, total_bytes); do {
@@ -905,6 +911,13 @@ bool blk_update_request(struct request *req, blk_status_t error, req->q->integrity.profile->complete_fn (req, nr_bytes);#endif +/* +* Upper layers may call blk_crypto_evict_key() anytime after the last +* bio_endio(). Therefore, the keyslot must be released before that. +*/ +if (blk_crypto_rq_has_keyslot (req) && nr_bytes >=blk_rq_bytes (req)) +__blk_crypto_rq_put_keyslot (req); +if (unlikely (error && !blk_rq_is_passthrough (req) && !(req->rq_flags &RQF_QUIET )) && !test_bit (GD_DEAD , &req->q->disk->state)) {
@@ -2967,7 +2980,7 @@ void blk_mq_submit_bio(struct bio *bio)blk_mq_bio_to_request (rq, bio, nr_segs); - ret =blk_crypto_init_request (rq); + ret =blk_crypto_rq_get_keyslot (rq);if (ret !=BLK_STS_OK ) { bio->bi_status = ret;bio_endio (bio);
Signature · 1 line
2.39.2
Eric Biggers
maintainerPATCH v3 2/6 blk-crypto: make blk_crypto_evict_key() return void
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
blk_crypto_evict_key() is only called in contexts such as inode eviction where failure is not an option. So there is nothing the caller can do with errors except log them. (dm-table.c does "use" the error code, but only to pass on to upper layers, so it doesn't really count.)
Just make blk_crypto_evict_key() return void and log errors itself.
--- block/blk-crypto.c | 20 +++++++++----------- drivers/md/dm-table.c | 19 +++++-------------- include/linux/blk-crypto.h | 4 ++-- 3 files changed, 16 insertions(+), 27 deletions(-)
block/blk-crypto.c +9 -11
@@ -13,6 +13,7 @@#include <linux/blkdev.h> #include <linux/blk-crypto-profile.h> #include <linux/module.h> +#include <linux/ratelimit.h> #include <linux/slab.h> #include "blk-crypto-internal.h"
@@ -408,21 +409,18 @@ int blk_crypto_start_using_key(struct block_device *bdev,* Upper layers (filesystems) must call this function to ensure that a key is * evicted from any hardware that it might have been programmed into. The key * must not be in use by any in-flight IO when this function is called. -* -* Return: 0 on success or if the key wasn't in any keyslot; -errno on error. */ -int blk_crypto_evict_key (struct block_device *bdev, -const struct blk_crypto_key *key) +void blk_crypto_evict_key (struct block_device *bdev, +const struct blk_crypto_key *key) {struct request_queue *q =bdev_get_queue (bdev); +int err;if (blk_crypto_config_supported_natively (bdev, &key->crypto_cfg)) -return __blk_crypto_evict_key (q->crypto_profile, key); - -/* -* If the block_device didn't support the key, then blk-crypto-fallback -* may have been used, so try to evict the key from blk-crypto-fallback. -*/ -return blk_crypto_fallback_evict_key (key); + err =__blk_crypto_evict_key (q->crypto_profile, key); +else + err =blk_crypto_fallback_evict_key (key); +if (err) +pr_warn_ratelimited ("%pg: error %d evicting key\n" , bdev, err); }EXPORT_SYMBOL_GPL (blk_crypto_evict_key);
drivers/md/dm-table.c +5 -14
@@ -1202,21 +1202,12 @@ struct dm_crypto_profile {struct mapped_device *md; }; -struct dm_keyslot_evict_args { -const struct blk_crypto_key *key; -int err; -}; -static int dm_keyslot_evict_callback (struct dm_target *ti,struct dm_dev *dev,sector_t start,sector_t len,void *data) { -struct dm_keyslot_evict_args *args = data; -int err; +const struct blk_crypto_key *key = data; - err =blk_crypto_evict_key (dev->bdev, args->key); -if (!args->err) - args->err = err; -/* Always try to evict the key from all devices. */ +blk_crypto_evict_key (dev->bdev, key);return 0 ; }
@@ -1229,7 +1220,6 @@ static int dm_keyslot_evict(struct blk_crypto_profile *profile, {struct mapped_device *md = container_of(profile, struct dm_crypto_profile, profile)->md; -struct dm_keyslot_evict_args args = { key };struct dm_table *t;int srcu_idx;
@@ -1242,11 +1232,12 @@ static int dm_keyslot_evict(struct blk_crypto_profile *profile,if (!ti->type->iterate_devices)continue ; - ti->type->iterate_devices (ti, dm_keyslot_evict_callback, &args); + ti->type->iterate_devices (ti, dm_keyslot_evict_callback, + (void *)key); }dm_put_live_table (md, srcu_idx); -return args.err; +return 0 ; } static int
include/linux/blk-crypto.h +2 -2
@@ -95,8 +95,8 @@ int blk_crypto_init_key(struct blk_crypto_key *blk_key, const u8 *raw_key,int blk_crypto_start_using_key (struct block_device *bdev,const struct blk_crypto_key *key); -int blk_crypto_evict_key (struct block_device *bdev, -const struct blk_crypto_key *key); +void blk_crypto_evict_key (struct block_device *bdev, +const struct blk_crypto_key *key);bool blk_crypto_config_supported_natively (struct block_device *bdev,const struct blk_crypto_config *cfg);
Signature · 1 line
2.39.2
Re: [PATCH v3 2/6] blk-crypto: make blk_crypto_evict_key() return void
Looks good:
Eric Biggers
maintainerPATCH v3 3/6 blk-crypto: make blk_crypto_evict_key() more robust
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
If blk_crypto_evict_key() sees that the key is still in-use (due to a bug) or that ->keyslot_evict failed, it currently just returns while leaving the key linked into the keyslot management structures.
However, blk_crypto_evict_key() is only called in contexts such as inode eviction where failure is not an option. So actually the caller proceeds with freeing the blk_crypto_key regardless of the return value of blk_crypto_evict_key().
These two assumptions don't match, and the result is that there can be a use-after-free in blk_crypto_reprogram_all_keys() after one of these errors occurs. (Note, these errors *shouldn't* happen; we're just talking about what happens if they do anyway.)
Fix this by making blk_crypto_evict_key() unlink the key from the keyslot management structures even on failure.
Also improve some comments.
--- block/blk-crypto-profile.c | 46 +++++++++++++++++--------------------- block/blk-crypto.c | 28 ++++++++++++++++------- 2 files changed, 41 insertions(+), 33 deletions(-)
block/blk-crypto-profile.c +21 -25
@@ -354,28 +354,16 @@ bool __blk_crypto_cfg_supported(struct blk_crypto_profile *profile,return true; } -/** -* __blk_crypto_evict_key() - Evict a key from a device. -* @profile: the crypto profile of the device -* @key: the key to evict. It must not still be used in any I/O. -* -* If the device has keyslots, this finds the keyslot (if any) that contains the -* specified key and calls the driver's keyslot_evict function to evict it. -* -* Otherwise, this just calls the driver's keyslot_evict function if it is -* implemented, passing just the key (without any particular keyslot). This -* allows layered devices to evict the key from their underlying devices. -* -* Context: Process context. Takes and releases profile->lock. -* Return: 0 on success or if there's no keyslot with the specified key, -EBUSY -* if the keyslot is still in use, or another -errno value on other -* error. +/* +* This is an internal function that evicts a key from an inline encryption +* device that can be either a real device or the blk-crypto-fallback "device". +* It is used only by blk_crypto_evict_key(); see that function for details. */ int __blk_crypto_evict_key (struct blk_crypto_profile *profile,const struct blk_crypto_key *key) {struct blk_crypto_keyslot *slot; -int err =0 ; +int err;if (profile->num_slots ==0 ) {if (profile->ll_ops.keyslot_evict) {
@@ -389,22 +377,30 @@ int __blk_crypto_evict_key(struct blk_crypto_profile *profile,blk_crypto_hw_enter (profile); slot =blk_crypto_find_keyslot (profile, key); -if (!slot) - goto out_unlock; +if (!slot) { +/* +* Not an error, since a key not in use by I/O is not guaranteed +* to be in a keyslot. There can be more keys than keyslots. +*/ + err =0 ; + goto out; + }if (WARN_ON_ONCE (atomic_read (&slot->slot_refs) !=0 )) { +/* BUG: key is still in use by I/O */ err = -EBUSY ; - goto out_unlock; + goto out_remove; } err = profile->ll_ops.keyslot_evict (profile, key,blk_crypto_keyslot_index (slot)); -if (err) - goto out_unlock; - +out_remove: +/* +* Callers free the key even on error, so unlink the key from the hash +* table and clear slot->key even on error. +*/ hlist_del (&slot->hash_node); slot->key =NULL ; - err =0 ; -out_unlock: +out:blk_crypto_hw_exit (profile);return err; }
block/blk-crypto.c +20 -8
@@ -400,15 +400,19 @@ int blk_crypto_start_using_key(struct block_device *bdev, }/** -* blk_crypto_evict_key() - Evict a key from any inline encryption hardware -* it may have been programmed into -* @bdev: The block_device who's associated inline encryption hardware this key -* might have been programmed into -* @key: The key to evict +* blk_crypto_evict_key() - Evict a blk_crypto_key from a block_device +* @bdev: a block_device on which I/O using the key may have been done +* @key: the key to evict * -* Upper layers (filesystems) must call this function to ensure that a key is -* evicted from any hardware that it might have been programmed into. The key -* must not be in use by any in-flight IO when this function is called. +* For a given block_device, this function removes the given blk_crypto_key from +* the keyslot management structures and evicts it from any underlying hardware +* keyslot(s) or blk-crypto-fallback keyslot it may have been programmed into. +* +* Upper layers must call this before freeing the blk_crypto_key. It must be +* called for every block_device the key may have been used on. The key must no +* longer be in use by any I/O when this function is called. +* +* Context: May sleep. */ void blk_crypto_evict_key (struct block_device *bdev,const struct blk_crypto_key *key)
@@ -420,6 +424,14 @@ void blk_crypto_evict_key(struct block_device *bdev, err =__blk_crypto_evict_key (q->crypto_profile, key); else err =blk_crypto_fallback_evict_key (key); +/* +* An error can only occur here if the key failed to be evicted from a +* keyslot (due to a hardware or driver issue) or is allegedly still in +* use by I/O (due to a kernel bug). Even in these cases, the key is +* still unlinked from the keyslot management structures, and the caller +* is allowed and expected to free it right away. There's nothing +* callers can do to handle errors, so just log them and return void. +*/ if (err)pr_warn_ratelimited ("%pg: error %d evicting key\n" , bdev, err); }
Signature · 1 line
2.39.2
Re: [PATCH v3 3/6] blk-crypto: make blk_crypto_evict_key() more robust
Looks good:
Eric Biggers
maintainerPATCH v3 4/6 blk-crypto: remove blk_crypto_insert_cloned_request()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
blk_crypto_insert_cloned_request() is the same as blk_crypto_rq_get_keyslot(), so just use that directly.
--- Documentation/block/inline-encryption.rst | 3 +-- block/blk-crypto-internal.h | 15 --------------- block/blk-mq.c | 2 +- 3 files changed, 2 insertions(+), 18 deletions(-)
Documentation/block/inline-encryption.rst +1 -2
@@ -270,8 +270,7 @@ Request queue based layered devices like dm-rq that wish to support inline encryption need to create their own blk_crypto_profile for their request_queue, and expose whatever functionality they choose. When a layered device wants to pass a clone of that request to another request_queue, blk-crypto will -initialize and prepare the clone as necessary; see -``blk_crypto_insert_cloned_request()``. +initialize and prepare the clone as necessary. Interaction between inline encryption and blk integrity =======================================================
block/blk-crypto-internal.h +0 -15
@@ -205,21 +205,6 @@ static inline int blk_crypto_rq_bio_prep(struct request *rq, struct bio *bio,return 0 ; } -/** -* blk_crypto_insert_cloned_request - Prepare a cloned request to be inserted -* into a request queue. -* @rq: the request being queued -* -* Return: BLK_STS_OK on success, nonzero on error. -*/ -static inline blk_status_t blk_crypto_insert_cloned_request (struct request *rq) -{ - -if (blk_crypto_rq_is_encrypted (rq)) -return blk_crypto_rq_get_keyslot (rq); -return BLK_STS_OK ; -} -#ifdef CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK int blk_crypto_fallback_start_using_mode (enum blk_crypto_mode_num mode_num);
block/blk-mq.c +1 -1
@@ -3049,7 +3049,7 @@ blk_status_t blk_insert_cloned_request(struct request *rq)if (q->disk &&should_fail_request (q->disk->part0,blk_rq_bytes (rq)))return BLK_STS_IOERR ; -if (blk_crypto_insert_cloned_request (rq)) +if (blk_crypto_rq_get_keyslot (rq))return BLK_STS_IOERR ;blk_account_io_start (rq);
Signature · 1 line
2.39.2
Re: [PATCH v3 4/6] blk-crypto: remove blk_crypto_insert_cloned_request()
Looks good:
Eric Biggers
maintainerPATCH v3 5/6 blk-mq: return actual keyslot error in blk_insert_cloned_request()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
To avoid hiding information, pass on the error code from blk_crypto_rq_get_keyslot() instead of always using BLK_STS_IOERR.
--- block/blk-mq.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
block/blk-mq.c +3 -2
@@ -3049,8 +3049,9 @@ blk_status_t blk_insert_cloned_request(struct request *rq)if (q->disk &&should_fail_request (q->disk->part0,blk_rq_bytes (rq)))return BLK_STS_IOERR ; -if (blk_crypto_rq_get_keyslot (rq)) -return BLK_STS_IOERR ; + ret =blk_crypto_rq_get_keyslot (rq); +if (ret !=BLK_STS_OK ) +return ret;blk_account_io_start (rq);
Signature · 1 line
2.39.2
Jens Axboe
maintainerRe: [PATCH v3 5/6] blk-mq: return actual keyslot error in blk_insert_cloned_request()
Quoted 4 lines · Eric Biggers
From: Eric Biggers <[email protected]>
To avoid hiding information, pass on the error code from
blk_crypto_rq_get_keyslot()instead of always usingBLK_STS_IOERR.
Maybe just fold this with the previous patch?
Signature · 1 line
Jens Axboe
Eric Biggers
maintainerRe: [PATCH v3 5/6] blk-mq: return actual keyslot error in blk_insert_cloned_request()
Quoted 7 lines · Jens Axboe
Quoted 4 lines · Eric Biggers
From: Eric Biggers <[email protected]>
To avoid hiding information, pass on the error code from
blk_crypto_rq_get_keyslot()instead of always usingBLK_STS_IOERR.Maybe just fold this with the previous patch?
I'd prefer to keep the behavior change separate from the cleanup.
- Eric
Jens Axboe
maintainerRe: [PATCH v3 5/6] blk-mq: return actual keyslot error in blk_insert_cloned_request()
Quoted 10 lines · Eric Biggers
Quoted 7 lines · Jens Axboe
Quoted 4 lines · Eric Biggers
From: Eric Biggers <[email protected]>
To avoid hiding information, pass on the error code from
blk_crypto_rq_get_keyslot()instead of always usingBLK_STS_IOERR.Maybe just fold this with the previous patch?
I'd prefer to keep the behavior change separate from the cleanup.
OK fair enough, not a big deal to me. Series looks fine as far as I'm concerned. Not loving the extra additions in the completion path, but I suppose there's no way around that.
Signature · 1 line
Jens Axboe
Eric Biggers
maintainerRe: [PATCH v3 5/6] blk-mq: return actual keyslot error in blk_insert_cloned_request()
Quoted 8 lines · Jens Axboe
Quoted 7 lines · Eric Biggers
Quoted 6 lines · Jens Axboe
Quoted 4 lines · Eric Biggers
From: Eric Biggers <[email protected]>
To avoid hiding information, pass on the error code from
blk_crypto_rq_get_keyslot()instead of always usingBLK_STS_IOERR.
Quoted 7 lines
Quoted 3 lines
Quoted 1 line
Maybe just fold this with the previous patch?
I'd prefer to keep the behavior change separate from the cleanup.
OK fair enough, not a big deal to me. Series looks fine as far as I'm concerned. Not loving the extra additions in the completion path, but I suppose there's no way around that.
Well, my first patch didn't have that:
https://lore.kernel.org/linux-block/[email protected]
But it didn't fix the keyslot refcounting work as expected, which Nathan didn't like (and I agree with that).
- Eric
Re: [PATCH v3 5/6] blk-mq: return actual keyslot error in blk_insert_cloned_request()
In reply to Eric BiggersLooks good:
Eric Biggers
maintainerPATCH v3 6/6 blk-crypto: drop the NULL check from blk_crypto_put_keyslot()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
Now that all callers of blk_crypto_put_keyslot() check for NULL before calling it, there is no need for blk_crypto_put_keyslot() to do the NULL check itself.
--- block/blk-crypto-profile.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-)
block/blk-crypto-profile.c +4 -10
@@ -227,14 +227,13 @@ EXPORT_SYMBOL_GPL(blk_crypto_keyslot_index);* @profile: the crypto profile of the device the key will be used on * @key: the key that will be used * @slot_ptr: If a keyslot is allocated, an opaque pointer to the keyslot struct -* will be stored here; otherwise NULL will be stored here. +* will be stored here. blk_crypto_put_keyslot() must be called +* later to release it. Otherwise, NULL will be stored here. * * If the device has keyslots, this gets a keyslot that's been programmed with * the specified key. If the key is already in a slot, this reuses it; * otherwise this waits for a slot to become idle and programs the key into it. * -* This must be paired with a call to blk_crypto_put_keyslot(). -* * Context: Process context. Takes and releases profile->lock. * Return: BLK_STS_OK on success, meaning that either a keyslot was allocated or * one wasn't needed; or a blk_status_t error on failure.
@@ -312,20 +311,15 @@ blk_status_t blk_crypto_get_keyslot(struct blk_crypto_profile *profile,/** * blk_crypto_put_keyslot() - Release a reference to a keyslot -* @slot: The keyslot to release the reference of (may be NULL). +* @slot: The keyslot to release the reference of * * Context: Any context. */ void blk_crypto_put_keyslot (struct blk_crypto_keyslot *slot) { -struct blk_crypto_profile *profile; +struct blk_crypto_profile *profile = slot->profile;unsigned long flags; -if (!slot) -return ; - - profile = slot->profile; -if (atomic_dec_and_lock_irqsave (&slot->slot_refs, &profile->idle_slots_lock, flags)) {list_add_tail (&slot->idle_slot_node, &profile->idle_slots);
Signature · 1 line
2.39.2
Quoted 8 lines · Eric Biggers
This series fixes a race condition in blk-crypto keyslot management and makes some related cleanups. It replaces
"[PATCH] blk-crypto: makeblk_crypto_evict_key()always try to evict"
(https://lore.kernel.org/r/[email protected]),
which was a simpler fix but didn't fix the keyslot reference counting to work as expected.[...]
Applied, thanks!
[1/6] blk-mq: release crypto keyslot before reporting I/O complete
(no commit info)
[2/6] blk-crypto: make blk_crypto_evict_key() return void
(no commit info)
[3/6] blk-crypto: make blk_crypto_evict_key() more robust
(no commit info)
[4/6] blk-crypto: remove blk_crypto_insert_cloned_request()
(no commit info)
[5/6] blk-mq: return actual keyslot error in blk_insert_cloned_request()
(no commit info)
[6/6] blk-crypto: drop the NULL check from blk_crypto_put_keyslot()
(no commit info)
Best regards,
Signature · 1 line
Jens Axboe