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

Configure Feed

Select the types of activity you want to include in your feed.

smb: client: fix potential UAF in smb2_close_cached_fid()

find_or_create_cached_dir() could grab a new reference after kref_put()
had seen the refcount drop to zero but before cfid_list_lock is acquired
in smb2_close_cached_fid(), leading to use-after-free.

Switch to kref_put_lock() so cfid_release() is called with
cfid_list_lock held, closing that gap.

Fixes: ebe98f1447bb ("cifs: enable caching of directories for which a lease is held")
Cc: stable@vger.kernel.org
Reported-by: Jay Shin <jaeshin@redhat.com>
Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Henrique Carvalho and committed by
Steve French
734e9962 6146a0f1

+9 -7
+9 -7
fs/smb/client/cached_dir.c
··· 388 388 * lease. Release one here, and the second below. 389 389 */ 390 390 cfid->has_lease = false; 391 - kref_put(&cfid->refcount, smb2_close_cached_fid); 391 + close_cached_dir(cfid); 392 392 } 393 393 spin_unlock(&cfids->cfid_list_lock); 394 394 395 - kref_put(&cfid->refcount, smb2_close_cached_fid); 395 + close_cached_dir(cfid); 396 396 } else { 397 397 *ret_cfid = cfid; 398 398 atomic_inc(&tcon->num_remote_opens); ··· 438 438 439 439 static void 440 440 smb2_close_cached_fid(struct kref *ref) 441 + __releases(&cfid->cfids->cfid_list_lock) 441 442 { 442 443 struct cached_fid *cfid = container_of(ref, struct cached_fid, 443 444 refcount); 444 445 int rc; 445 446 446 - spin_lock(&cfid->cfids->cfid_list_lock); 447 + lockdep_assert_held(&cfid->cfids->cfid_list_lock); 448 + 447 449 if (cfid->on_list) { 448 450 list_del(&cfid->entry); 449 451 cfid->on_list = false; ··· 480 478 spin_lock(&cfid->cfids->cfid_list_lock); 481 479 if (cfid->has_lease) { 482 480 cfid->has_lease = false; 483 - kref_put(&cfid->refcount, smb2_close_cached_fid); 481 + close_cached_dir(cfid); 484 482 } 485 483 spin_unlock(&cfid->cfids->cfid_list_lock); 486 484 close_cached_dir(cfid); ··· 489 487 490 488 void close_cached_dir(struct cached_fid *cfid) 491 489 { 492 - kref_put(&cfid->refcount, smb2_close_cached_fid); 490 + kref_put_lock(&cfid->refcount, smb2_close_cached_fid, &cfid->cfids->cfid_list_lock); 493 491 } 494 492 495 493 /* ··· 598 596 599 597 WARN_ON(cfid->on_list); 600 598 601 - kref_put(&cfid->refcount, smb2_close_cached_fid); 599 + close_cached_dir(cfid); 602 600 cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cached_close); 603 601 } 604 602 ··· 764 762 * Drop the ref-count from above, either the lease-ref (if there 765 763 * was one) or the extra one acquired. 766 764 */ 767 - kref_put(&cfid->refcount, smb2_close_cached_fid); 765 + close_cached_dir(cfid); 768 766 } 769 767 queue_delayed_work(cfid_put_wq, &cfids->laundromat_work, 770 768 dir_cache_timeout * HZ);