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

cifs: Fix leak when handling lease break for cached root fid

Handling a lease break for the cached root didn't free the
smb2_lease_break_work allocation, resulting in a leak:

unreferenced object 0xffff98383a5af480 (size 128):
comm "cifsd", pid 684, jiffies 4294936606 (age 534.868s)
hex dump (first 32 bytes):
c0 ff ff ff 1f 00 00 00 88 f4 5a 3a 38 98 ff ff ..........Z:8...
88 f4 5a 3a 38 98 ff ff 80 88 d6 8a ff ff ff ff ..Z:8...........
backtrace:
[<0000000068957336>] smb2_is_valid_oplock_break+0x1fa/0x8c0
[<0000000073b70b9e>] cifs_demultiplex_thread+0x73d/0xcc0
[<00000000905fa372>] kthread+0x11c/0x150
[<0000000079378e4e>] ret_from_fork+0x22/0x30

Avoid this leak by only allocating when necessary.

Fixes: a93864d93977 ("cifs: add lease tracking to the cached root fid")
Signed-off-by: Paul Aurich <paul@darkrain42.org>
CC: Stable <stable@vger.kernel.org> # v4.18+
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Paul Aurich and committed by
Steve French
baf57b56 bcf87687

+53 -22
+53 -22
fs/cifs/smb2misc.c
··· 508 508 kfree(lw); 509 509 } 510 510 511 - static bool 512 - smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, 513 - struct smb2_lease_break_work *lw) 511 + static void 512 + smb2_queue_pending_open_break(struct tcon_link *tlink, __u8 *lease_key, 513 + __le32 new_lease_state) 514 514 { 515 - bool found; 515 + struct smb2_lease_break_work *lw; 516 + 517 + lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); 518 + if (!lw) { 519 + cifs_put_tlink(tlink); 520 + return; 521 + } 522 + 523 + INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); 524 + lw->tlink = tlink; 525 + lw->lease_state = new_lease_state; 526 + memcpy(lw->lease_key, lease_key, SMB2_LEASE_KEY_SIZE); 527 + queue_work(cifsiod_wq, &lw->lease_break); 528 + } 529 + 530 + static bool 531 + smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp) 532 + { 516 533 __u8 lease_state; 517 534 struct list_head *tmp; 518 535 struct cifsFileInfo *cfile; 519 - struct cifs_pending_open *open; 520 536 struct cifsInodeInfo *cinode; 521 537 int ack_req = le32_to_cpu(rsp->Flags & 522 538 SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); ··· 562 546 cfile->oplock_level = lease_state; 563 547 564 548 cifs_queue_oplock_break(cfile); 565 - kfree(lw); 566 549 return true; 567 550 } 568 551 569 - found = false; 552 + return false; 553 + } 554 + 555 + static struct cifs_pending_open * 556 + smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon, 557 + struct smb2_lease_break *rsp) 558 + { 559 + __u8 lease_state = le32_to_cpu(rsp->NewLeaseState); 560 + int ack_req = le32_to_cpu(rsp->Flags & 561 + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED); 562 + struct cifs_pending_open *open; 563 + struct cifs_pending_open *found = NULL; 564 + 570 565 list_for_each_entry(open, &tcon->pending_opens, olist) { 571 566 if (memcmp(open->lease_key, rsp->LeaseKey, 572 567 SMB2_LEASE_KEY_SIZE)) 573 568 continue; 574 569 575 570 if (!found && ack_req) { 576 - found = true; 577 - memcpy(lw->lease_key, open->lease_key, 578 - SMB2_LEASE_KEY_SIZE); 579 - lw->tlink = cifs_get_tlink(open->tlink); 580 - queue_work(cifsiod_wq, &lw->lease_break); 571 + found = open; 581 572 } 582 573 583 574 cifs_dbg(FYI, "found in the pending open list\n"); ··· 605 582 struct TCP_Server_Info *server; 606 583 struct cifs_ses *ses; 607 584 struct cifs_tcon *tcon; 608 - struct smb2_lease_break_work *lw; 609 - 610 - lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL); 611 - if (!lw) 612 - return false; 613 - 614 - INIT_WORK(&lw->lease_break, cifs_ses_oplock_break); 615 - lw->lease_state = rsp->NewLeaseState; 585 + struct cifs_pending_open *open; 616 586 617 587 cifs_dbg(FYI, "Checking for lease break\n"); 618 588 ··· 623 607 spin_lock(&tcon->open_file_lock); 624 608 cifs_stats_inc( 625 609 &tcon->stats.cifs_stats.num_oplock_brks); 626 - if (smb2_tcon_has_lease(tcon, rsp, lw)) { 610 + if (smb2_tcon_has_lease(tcon, rsp)) { 627 611 spin_unlock(&tcon->open_file_lock); 628 612 spin_unlock(&cifs_tcp_ses_lock); 613 + return true; 614 + } 615 + open = smb2_tcon_find_pending_open_lease(tcon, 616 + rsp); 617 + if (open) { 618 + __u8 lease_key[SMB2_LEASE_KEY_SIZE]; 619 + struct tcon_link *tlink; 620 + 621 + tlink = cifs_get_tlink(open->tlink); 622 + memcpy(lease_key, open->lease_key, 623 + SMB2_LEASE_KEY_SIZE); 624 + spin_unlock(&tcon->open_file_lock); 625 + spin_unlock(&cifs_tcp_ses_lock); 626 + smb2_queue_pending_open_break(tlink, 627 + lease_key, 628 + rsp->NewLeaseState); 629 629 return true; 630 630 } 631 631 spin_unlock(&tcon->open_file_lock); ··· 661 629 } 662 630 } 663 631 spin_unlock(&cifs_tcp_ses_lock); 664 - kfree(lw); 665 632 cifs_dbg(FYI, "Can not process lease break - no lease matched\n"); 666 633 return false; 667 634 }