lkml.org 
[lkml]   [2020]   [Aug]   [20]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.4 035/152] cifs: Fix leak when handling lease break for cached root fid
    Date
    From: Paul Aurich <paul@darkrain42.org>

    commit baf57b56d3604880ccb3956ec6c62ea894f5de99 upstream.

    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>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    fs/cifs/smb2misc.c | 73 +++++++++++++++++++++++++++++++++++++----------------
    1 file changed, 52 insertions(+), 21 deletions(-)

    --- a/fs/cifs/smb2misc.c
    +++ b/fs/cifs/smb2misc.c
    @@ -509,15 +509,31 @@ cifs_ses_oplock_break(struct work_struct
    kfree(lw);
    }

    +static void
    +smb2_queue_pending_open_break(struct tcon_link *tlink, __u8 *lease_key,
    + __le32 new_lease_state)
    +{
    + struct smb2_lease_break_work *lw;
    +
    + lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
    + if (!lw) {
    + cifs_put_tlink(tlink);
    + return;
    + }
    +
    + INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
    + lw->tlink = tlink;
    + lw->lease_state = new_lease_state;
    + memcpy(lw->lease_key, lease_key, SMB2_LEASE_KEY_SIZE);
    + queue_work(cifsiod_wq, &lw->lease_break);
    +}
    +
    static bool
    -smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
    - struct smb2_lease_break_work *lw)
    +smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp)
    {
    - bool found;
    __u8 lease_state;
    struct list_head *tmp;
    struct cifsFileInfo *cfile;
    - struct cifs_pending_open *open;
    struct cifsInodeInfo *cinode;
    int ack_req = le32_to_cpu(rsp->Flags &
    SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
    @@ -556,22 +572,29 @@ smb2_tcon_has_lease(struct cifs_tcon *tc
    &cinode->flags);

    cifs_queue_oplock_break(cfile);
    - kfree(lw);
    return true;
    }

    - found = false;
    + return false;
    +}
    +
    +static struct cifs_pending_open *
    +smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon,
    + struct smb2_lease_break *rsp)
    +{
    + __u8 lease_state = le32_to_cpu(rsp->NewLeaseState);
    + int ack_req = le32_to_cpu(rsp->Flags &
    + SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
    + struct cifs_pending_open *open;
    + struct cifs_pending_open *found = NULL;
    +
    list_for_each_entry(open, &tcon->pending_opens, olist) {
    if (memcmp(open->lease_key, rsp->LeaseKey,
    SMB2_LEASE_KEY_SIZE))
    continue;

    if (!found && ack_req) {
    - found = true;
    - memcpy(lw->lease_key, open->lease_key,
    - SMB2_LEASE_KEY_SIZE);
    - lw->tlink = cifs_get_tlink(open->tlink);
    - queue_work(cifsiod_wq, &lw->lease_break);
    + found = open;
    }

    cifs_dbg(FYI, "found in the pending open list\n");
    @@ -592,14 +615,7 @@ smb2_is_valid_lease_break(char *buffer)
    struct TCP_Server_Info *server;
    struct cifs_ses *ses;
    struct cifs_tcon *tcon;
    - struct smb2_lease_break_work *lw;
    -
    - lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
    - if (!lw)
    - return false;
    -
    - INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
    - lw->lease_state = rsp->NewLeaseState;
    + struct cifs_pending_open *open;

    cifs_dbg(FYI, "Checking for lease break\n");

    @@ -617,11 +633,27 @@ smb2_is_valid_lease_break(char *buffer)
    spin_lock(&tcon->open_file_lock);
    cifs_stats_inc(
    &tcon->stats.cifs_stats.num_oplock_brks);
    - if (smb2_tcon_has_lease(tcon, rsp, lw)) {
    + if (smb2_tcon_has_lease(tcon, rsp)) {
    spin_unlock(&tcon->open_file_lock);
    spin_unlock(&cifs_tcp_ses_lock);
    return true;
    }
    + open = smb2_tcon_find_pending_open_lease(tcon,
    + rsp);
    + if (open) {
    + __u8 lease_key[SMB2_LEASE_KEY_SIZE];
    + struct tcon_link *tlink;
    +
    + tlink = cifs_get_tlink(open->tlink);
    + memcpy(lease_key, open->lease_key,
    + SMB2_LEASE_KEY_SIZE);
    + spin_unlock(&tcon->open_file_lock);
    + spin_unlock(&cifs_tcp_ses_lock);
    + smb2_queue_pending_open_break(tlink,
    + lease_key,
    + rsp->NewLeaseState);
    + return true;
    + }
    spin_unlock(&tcon->open_file_lock);

    if (tcon->crfid.is_valid &&
    @@ -639,7 +671,6 @@ smb2_is_valid_lease_break(char *buffer)
    }
    }
    spin_unlock(&cifs_tcp_ses_lock);
    - kfree(lw);
    cifs_dbg(FYI, "Can not process lease break - no lease matched\n");
    return false;
    }

    \
     
     \ /
      Last update: 2020-08-20 14:43    [W:4.097 / U:0.268 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site