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

fs, nfs: convert pnfs_layout_hdr.plh_refcount 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 pnfs_layout_hdr.plh_refcount 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
2b28a7be eba6dd69

+7 -7
+6 -6
fs/nfs/pnfs.c
··· 251 251 void 252 252 pnfs_get_layout_hdr(struct pnfs_layout_hdr *lo) 253 253 { 254 - atomic_inc(&lo->plh_refcount); 254 + refcount_inc(&lo->plh_refcount); 255 255 } 256 256 257 257 static struct pnfs_layout_hdr * ··· 296 296 297 297 pnfs_layoutreturn_before_put_layout_hdr(lo); 298 298 299 - if (atomic_dec_and_lock(&lo->plh_refcount, &inode->i_lock)) { 299 + if (refcount_dec_and_lock(&lo->plh_refcount, &inode->i_lock)) { 300 300 if (!list_empty(&lo->plh_segs)) 301 301 WARN_ONCE(1, "NFS: BUG unfreed layout segments.\n"); 302 302 pnfs_detach_layout_hdr(lo); ··· 395 395 { 396 396 lo->plh_retry_timestamp = jiffies; 397 397 if (!test_and_set_bit(fail_bit, &lo->plh_flags)) 398 - atomic_inc(&lo->plh_refcount); 398 + refcount_inc(&lo->plh_refcount); 399 399 } 400 400 401 401 static void 402 402 pnfs_layout_clear_fail_bit(struct pnfs_layout_hdr *lo, int fail_bit) 403 403 { 404 404 if (test_and_clear_bit(fail_bit, &lo->plh_flags)) 405 - atomic_dec(&lo->plh_refcount); 405 + refcount_dec(&lo->plh_refcount); 406 406 } 407 407 408 408 static void ··· 472 472 WARN_ON(test_bit(NFS_LSEG_VALID, &lseg->pls_flags)); 473 473 list_del_init(&lseg->pls_list); 474 474 /* Matched by pnfs_get_layout_hdr in pnfs_layout_insert_lseg */ 475 - atomic_dec(&lo->plh_refcount); 475 + refcount_dec(&lo->plh_refcount); 476 476 if (test_bit(NFS_LSEG_LAYOUTRETURN, &lseg->pls_flags)) 477 477 return; 478 478 if (list_empty(&lo->plh_segs) && ··· 1451 1451 lo = pnfs_alloc_layout_hdr(ino, gfp_flags); 1452 1452 if (!lo) 1453 1453 return NULL; 1454 - atomic_set(&lo->plh_refcount, 1); 1454 + refcount_set(&lo->plh_refcount, 1); 1455 1455 INIT_LIST_HEAD(&lo->plh_layouts); 1456 1456 INIT_LIST_HEAD(&lo->plh_segs); 1457 1457 INIT_LIST_HEAD(&lo->plh_return_segs);
+1 -1
fs/nfs/pnfs.h
··· 180 180 }; 181 181 182 182 struct pnfs_layout_hdr { 183 - atomic_t plh_refcount; 183 + refcount_t plh_refcount; 184 184 atomic_t plh_outstanding; /* number of RPCs out */ 185 185 struct list_head plh_layouts; /* other client layouts */ 186 186 struct list_head plh_bulk_destroy;