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

fs, nfs: convert nfs4_pnfs_ds.ds_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 nfs4_pnfs_ds.ds_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
a2a5dea7 3be0f80b

+7 -6
+2 -1
fs/nfs/pnfs.h
··· 30 30 #ifndef FS_NFS_PNFS_H 31 31 #define FS_NFS_PNFS_H 32 32 33 + #include <linux/refcount.h> 33 34 #include <linux/nfs_fs.h> 34 35 #include <linux/nfs_page.h> 35 36 #include <linux/workqueue.h> ··· 55 54 char *ds_remotestr; /* comma sep list of addrs */ 56 55 struct list_head ds_addrs; 57 56 struct nfs_client *ds_clp; 58 - atomic_t ds_count; 57 + refcount_t ds_count; 59 58 unsigned long ds_state; 60 59 #define NFS4DS_CONNECTING 0 /* ds is establishing connection */ 61 60 };
+5 -5
fs/nfs/pnfs_nfs.c
··· 338 338 " client %p\n" 339 339 " cl_exchange_flags %x\n", 340 340 ds->ds_remotestr, 341 - atomic_read(&ds->ds_count), ds->ds_clp, 341 + refcount_read(&ds->ds_count), ds->ds_clp, 342 342 ds->ds_clp ? ds->ds_clp->cl_exchange_flags : 0); 343 343 } 344 344 ··· 451 451 452 452 void nfs4_pnfs_ds_put(struct nfs4_pnfs_ds *ds) 453 453 { 454 - if (atomic_dec_and_lock(&ds->ds_count, 454 + if (refcount_dec_and_lock(&ds->ds_count, 455 455 &nfs4_ds_cache_lock)) { 456 456 list_del_init(&ds->ds_node); 457 457 spin_unlock(&nfs4_ds_cache_lock); ··· 537 537 INIT_LIST_HEAD(&ds->ds_addrs); 538 538 list_splice_init(dsaddrs, &ds->ds_addrs); 539 539 ds->ds_remotestr = remotestr; 540 - atomic_set(&ds->ds_count, 1); 540 + refcount_set(&ds->ds_count, 1); 541 541 INIT_LIST_HEAD(&ds->ds_node); 542 542 ds->ds_clp = NULL; 543 543 list_add(&ds->ds_node, &nfs4_data_server_cache); ··· 546 546 } else { 547 547 kfree(remotestr); 548 548 kfree(ds); 549 - atomic_inc(&tmp_ds->ds_count); 549 + refcount_inc(&tmp_ds->ds_count); 550 550 dprintk("%s data server %s found, inc'ed ds_count to %d\n", 551 551 __func__, tmp_ds->ds_remotestr, 552 - atomic_read(&tmp_ds->ds_count)); 552 + refcount_read(&tmp_ds->ds_count)); 553 553 ds = tmp_ds; 554 554 } 555 555 spin_unlock(&nfs4_ds_cache_lock);