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: e37a784d8b ("fscrypt: use READ_ONCE() to access ->i_crypt_info")
Link: https://lore.kernel.org/r/20200721225920.114347-5-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
This commit is contained in:
Eric Biggers 2020-07-21 15:59:19 -07:00
parent 777afe4e68
commit ab673b9874
3 changed files with 37 additions and 8 deletions

View file

@ -74,10 +74,15 @@ struct fscrypt_operations {
struct request_queue **devs);
};
static inline bool fscrypt_has_encryption_key(const struct inode *inode)
static inline struct fscrypt_info *fscrypt_get_info(const struct inode *inode)
{
/* pairs with cmpxchg_release() in fscrypt_get_encryption_info() */
return READ_ONCE(inode->i_crypt_info) != NULL;
/*
* Pairs with the cmpxchg_release() in fscrypt_get_encryption_info().
* I.e., another task may publish ->i_crypt_info concurrently, executing
* a RELEASE barrier. We need to use smp_load_acquire() here to safely
* ACQUIRE the memory the other task published.
*/
return smp_load_acquire(&inode->i_crypt_info);
}
/**
@ -234,9 +239,9 @@ static inline void fscrypt_set_ops(struct super_block *sb,
}
#else /* !CONFIG_FS_ENCRYPTION */
static inline bool fscrypt_has_encryption_key(const struct inode *inode)
static inline struct fscrypt_info *fscrypt_get_info(const struct inode *inode)
{
return false;
return NULL;
}
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)
!__fscrypt_inode_uses_inline_crypto(inode);
}
/**
* fscrypt_has_encryption_key() - check whether an inode has had its key set up
* @inode: the inode to check
*
* Return: %true if the inode has had its encryption key set up, else %false.
*
* Usually this should be preceded by fscrypt_get_encryption_info() to try to
* set up the key first.
*/
static inline bool fscrypt_has_encryption_key(const struct inode *inode)
{
return fscrypt_get_info(inode) != NULL;
}
/**
* fscrypt_require_key() - require an inode's encryption key
* @inode: the inode we need the key for