diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/verity/fsverity_private.h | 15 | ||||
| -rw-r--r-- | fs/verity/open.c | 13 |
2 files changed, 26 insertions, 2 deletions
diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index 881d46f25e08..6f85f63861aa 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -11,6 +11,7 @@ #define pr_fmt(fmt) "fs-verity: " fmt #include <linux/fsverity.h> +#include <linux/rcupdate.h> #include <linux/rhashtable.h> /* @@ -76,7 +77,19 @@ struct merkle_tree_params { struct fsverity_info { struct rhash_head rhash_head; struct merkle_tree_params tree_params; - u8 root_hash[FS_VERITY_MAX_DIGEST_SIZE]; + union { + u8 root_hash[FS_VERITY_MAX_DIGEST_SIZE]; + + /* + * Used for final freeing of the struct, in which case root_hash + * is no longer needed. Specifically, the only fields needed + * besides the rcu_head are those needed by fsverity_free_info() + * itself, and those that can be accessed by fsverity_get_info() + * (rhash_head and inode) during the RCU-protected rhashtable + * chain search trying to find another inode's fsverity_info. + */ + struct rcu_head rcu_head; + }; u8 file_digest[FS_VERITY_MAX_DIGEST_SIZE]; struct inode *inode; unsigned long *hash_block_verified; diff --git a/fs/verity/open.c b/fs/verity/open.c index d0c56a7faa3b..52c8cd014921 100644 --- a/fs/verity/open.c +++ b/fs/verity/open.c @@ -399,11 +399,22 @@ void fsverity_free_info(struct fsverity_info *vi) kmem_cache_free(fsverity_info_cachep, vi); } +static void fsverity_free_info_rcu(struct rcu_head *head) +{ + fsverity_free_info(container_of(head, struct fsverity_info, rcu_head)); +} + void fsverity_remove_info(struct fsverity_info *vi) { rhashtable_remove_fast(&fsverity_info_hash, &vi->rhash_head, fsverity_info_hash_params); - fsverity_free_info(vi); + /* + * The freeing must be RCU-delayed because __fsverity_get_info() for a + * *different* inode's fsverity_info in the same rhashtable hash chain + * can still be accessing *this* fsverity_info's rhash_head and inode + * fields as part of the RCU-protected hash chain search. + */ + call_rcu(&vi->rcu_head, fsverity_free_info_rcu); } void fsverity_cleanup_inode(struct inode *inode) |
