Skip to content

Commit ab673b9

Browse files
committed
fscrypt: use smp_load_acquire() for ->i_crypt_info
Normally smp_store_release() or cmpxchg_release() is paired with smp_load_acquire(). Sometimes smp_load_acquire() can be replaced with the more lightweight READ_ONCE(). However, for this to be safe, all the published memory must only be accessed in a way that involves the pointer itself. This may not be the case if allocating the object also involves initializing a static or global variable, for example. fscrypt_info includes various sub-objects which are internal to and are allocated by other kernel subsystems such as keyrings and crypto. So by using READ_ONCE() for ->i_crypt_info, we're relying on internal implementation details of these other kernel subsystems. Remove this fragile assumption by using smp_load_acquire() instead. (Note: I haven't seen any real-world problems here. This change is just fixing the code to be guaranteed correct and less fragile.) Fixes: e37a784 ("fscrypt: use READ_ONCE() to access ->i_crypt_info") Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Eric Biggers <[email protected]>
1 parent 777afe4 commit ab673b9

File tree

3 files changed

+37
-8
lines changed

3 files changed

+37
-8
lines changed

fs/crypto/keysetup.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,17 @@ int fscrypt_get_encryption_info(struct inode *inode)
518518
if (res)
519519
goto out;
520520

521+
/*
522+
* Multiple tasks may race to set ->i_crypt_info, so use
523+
* cmpxchg_release(). This pairs with the smp_load_acquire() in
524+
* fscrypt_get_info(). I.e., here we publish ->i_crypt_info with a
525+
* RELEASE barrier so that other tasks can ACQUIRE it.
526+
*/
521527
if (cmpxchg_release(&inode->i_crypt_info, NULL, crypt_info) == NULL) {
528+
/*
529+
* We won the race and set ->i_crypt_info to our crypt_info.
530+
* Now link it into the master key's inode list.
531+
*/
522532
if (master_key) {
523533
struct fscrypt_master_key *mk =
524534
master_key->payload.data[0];
@@ -589,7 +599,7 @@ EXPORT_SYMBOL(fscrypt_free_inode);
589599
*/
590600
int fscrypt_drop_inode(struct inode *inode)
591601
{
592-
const struct fscrypt_info *ci = READ_ONCE(inode->i_crypt_info);
602+
const struct fscrypt_info *ci = fscrypt_get_info(inode);
593603
const struct fscrypt_master_key *mk;
594604

595605
/*

fs/crypto/policy.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ static int fscrypt_get_policy(struct inode *inode, union fscrypt_policy *policy)
352352
union fscrypt_context ctx;
353353
int ret;
354354

355-
ci = READ_ONCE(inode->i_crypt_info);
355+
ci = fscrypt_get_info(inode);
356356
if (ci) {
357357
/* key available, use the cached policy */
358358
*policy = ci->ci_policy;
@@ -641,7 +641,7 @@ int fscrypt_inherit_context(struct inode *parent, struct inode *child,
641641
if (res < 0)
642642
return res;
643643

644-
ci = READ_ONCE(parent->i_crypt_info);
644+
ci = fscrypt_get_info(parent);
645645
if (ci == NULL)
646646
return -ENOKEY;
647647

include/linux/fscrypt.h

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,15 @@ struct fscrypt_operations {
7474
struct request_queue **devs);
7575
};
7676

77-
static inline bool fscrypt_has_encryption_key(const struct inode *inode)
77+
static inline struct fscrypt_info *fscrypt_get_info(const struct inode *inode)
7878
{
79-
/* pairs with cmpxchg_release() in fscrypt_get_encryption_info() */
80-
return READ_ONCE(inode->i_crypt_info) != NULL;
79+
/*
80+
* Pairs with the cmpxchg_release() in fscrypt_get_encryption_info().
81+
* I.e., another task may publish ->i_crypt_info concurrently, executing
82+
* a RELEASE barrier. We need to use smp_load_acquire() here to safely
83+
* ACQUIRE the memory the other task published.
84+
*/
85+
return smp_load_acquire(&inode->i_crypt_info);
8186
}
8287

8388
/**
@@ -234,9 +239,9 @@ static inline void fscrypt_set_ops(struct super_block *sb,
234239
}
235240
#else /* !CONFIG_FS_ENCRYPTION */
236241

237-
static inline bool fscrypt_has_encryption_key(const struct inode *inode)
242+
static inline struct fscrypt_info *fscrypt_get_info(const struct inode *inode)
238243
{
239-
return false;
244+
return NULL;
240245
}
241246

242247
static inline bool fscrypt_needs_contents_encryption(const struct inode *inode)
@@ -619,6 +624,20 @@ static inline bool fscrypt_inode_uses_fs_layer_crypto(const struct inode *inode)
619624
!__fscrypt_inode_uses_inline_crypto(inode);
620625
}
621626

627+
/**
628+
* fscrypt_has_encryption_key() - check whether an inode has had its key set up
629+
* @inode: the inode to check
630+
*
631+
* Return: %true if the inode has had its encryption key set up, else %false.
632+
*
633+
* Usually this should be preceded by fscrypt_get_encryption_info() to try to
634+
* set up the key first.
635+
*/
636+
static inline bool fscrypt_has_encryption_key(const struct inode *inode)
637+
{
638+
return fscrypt_get_info(inode) != NULL;
639+
}
640+
622641
/**
623642
* fscrypt_require_key() - require an inode's encryption key
624643
* @inode: the inode we need the key for

0 commit comments

Comments
 (0)