Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fs, nfs: convert nfs_lock_context.count from atomic_t to refcount_t

atomic_t variables are currently used to implement reference
counters with the following properties:
- counter is initialized to 1 using atomic_set()
- a resource is freed upon counter reaching zero
- once counter reaches zero, its further
increments aren't allowed
- counter schema uses basic atomic operations
(set, inc, inc_not_zero, dec_and_test, etc.)

Such atomic variables should be converted to a newly provided
refcount_t type and API that prevents accidental counter overflows
and underflows. This is important since overflows and underflows
can lead to use-after-free situation and be exploitable.

The variable nfs_lock_context.count is used as pure reference counter.
Convert it to refcount_t and fix up the operations.

Suggested-by: Kees Cook <keescook@chromium.org>
Reviewed-by: David Windsor <dwindsor@gmail.com>
Reviewed-by: Hans Liljestrand <ishkamiel@gmail.com>
Signed-off-by: Elena Reshetova <elena.reshetova@intel.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>

authored by

Elena Reshetova and committed by
Anna Schumaker
2f62b5aa 194bc1f4

+8 -7
+6 -6
fs/nfs/inode.c
··· 783 783 784 784 static void nfs_init_lock_context(struct nfs_lock_context *l_ctx) 785 785 { 786 - atomic_set(&l_ctx->count, 1); 786 + refcount_set(&l_ctx->count, 1); 787 787 l_ctx->lockowner = current->files; 788 788 INIT_LIST_HEAD(&l_ctx->list); 789 789 atomic_set(&l_ctx->io_count, 0); ··· 797 797 do { 798 798 if (pos->lockowner != current->files) 799 799 continue; 800 - atomic_inc(&pos->count); 800 + refcount_inc(&pos->count); 801 801 return pos; 802 802 } while ((pos = list_entry(pos->list.next, typeof(*pos), list)) != head); 803 803 return NULL; ··· 836 836 struct nfs_open_context *ctx = l_ctx->open_context; 837 837 struct inode *inode = d_inode(ctx->dentry); 838 838 839 - if (!atomic_dec_and_lock(&l_ctx->count, &inode->i_lock)) 839 + if (!refcount_dec_and_lock(&l_ctx->count, &inode->i_lock)) 840 840 return; 841 841 list_del(&l_ctx->list); 842 842 spin_unlock(&inode->i_lock); ··· 913 913 struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) 914 914 { 915 915 if (ctx != NULL) 916 - atomic_inc(&ctx->lock_context.count); 916 + refcount_inc(&ctx->lock_context.count); 917 917 return ctx; 918 918 } 919 919 EXPORT_SYMBOL_GPL(get_nfs_open_context); ··· 924 924 struct super_block *sb = ctx->dentry->d_sb; 925 925 926 926 if (!list_empty(&ctx->list)) { 927 - if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock)) 927 + if (!refcount_dec_and_lock(&ctx->lock_context.count, &inode->i_lock)) 928 928 return; 929 929 list_del(&ctx->list); 930 930 spin_unlock(&inode->i_lock); 931 - } else if (!atomic_dec_and_test(&ctx->lock_context.count)) 931 + } else if (!refcount_dec_and_test(&ctx->lock_context.count)) 932 932 return; 933 933 if (inode != NULL) 934 934 NFS_PROTO(inode)->close_context(ctx, is_sync);
+2 -1
include/linux/nfs_fs.h
··· 22 22 #include <linux/mm.h> 23 23 #include <linux/pagemap.h> 24 24 #include <linux/rbtree.h> 25 + #include <linux/refcount.h> 25 26 #include <linux/rwsem.h> 26 27 #include <linux/wait.h> 27 28 ··· 56 55 }; 57 56 58 57 struct nfs_lock_context { 59 - atomic_t count; 58 + refcount_t count; 60 59 struct list_head list; 61 60 struct nfs_open_context *open_context; 62 61 fl_owner_t lockowner;