Skip to content

Commit

Permalink
Merge tag '5.1-rc5-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6
Browse files Browse the repository at this point in the history
Pull smb3 fixes from Steve French:
 "Five small SMB3 fixes, all also for stable - an important fix for an
  oplock (lease) bug, a handle leak, and three bugs spotted by KASAN"

* tag '5.1-rc5-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  CIFS: keep FileInfo handle live during oplock break
  cifs: fix handle leak in smb2_query_symlink()
  cifs: Fix lease buffer length error
  cifs: Fix use-after-free in SMB2_read
  cifs: Fix use-after-free in SMB2_write
  • Loading branch information
torvalds committed Apr 17, 2019
2 parents fe5cdef + b98749c commit e53f31b
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 14 deletions.
2 changes: 2 additions & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,7 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
}

struct cifsFileInfo *cifsFileInfo_get(struct cifsFileInfo *cifs_file);
void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_hdlr);
void cifsFileInfo_put(struct cifsFileInfo *cifs_file);

#define CIFS_CACHE_READ_FLG 1
Expand Down Expand Up @@ -1855,6 +1856,7 @@ GLOBAL_EXTERN spinlock_t gidsidlock;
#endif /* CONFIG_CIFS_ACL */

void cifs_oplock_break(struct work_struct *work);
void cifs_queue_oplock_break(struct cifsFileInfo *cfile);

extern const struct slow_work_ops cifs_oplock_break_ops;
extern struct workqueue_struct *cifsiod_wq;
Expand Down
30 changes: 25 additions & 5 deletions fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,12 +360,30 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file)
return cifs_file;
}

/*
* Release a reference on the file private data. This may involve closing
* the filehandle out on the server. Must be called without holding
* tcon->open_file_lock and cifs_file->file_info_lock.
/**
* cifsFileInfo_put - release a reference of file priv data
*
* Always potentially wait for oplock handler. See _cifsFileInfo_put().
*/
void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
{
_cifsFileInfo_put(cifs_file, true);
}

/**
* _cifsFileInfo_put - release a reference of file priv data
*
* This may involve closing the filehandle @cifs_file out on the
* server. Must be called without holding tcon->open_file_lock and
* cifs_file->file_info_lock.
*
* If @wait_for_oplock_handler is true and we are releasing the last
* reference, wait for any running oplock break handler of the file
* and cancel any pending one. If calling this function from the
* oplock break handler, you need to pass false.
*
*/
void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
{
struct inode *inode = d_inode(cifs_file->dentry);
struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
Expand Down Expand Up @@ -414,7 +432,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)

spin_unlock(&tcon->open_file_lock);

oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break);
oplock_break_cancelled = wait_oplock_handler ?
cancel_work_sync(&cifs_file->oplock_break) : false;

if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
struct TCP_Server_Info *server = tcon->ses->server;
Expand Down Expand Up @@ -4603,6 +4622,7 @@ void cifs_oplock_break(struct work_struct *work)
cinode);
cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
}
_cifsFileInfo_put(cfile, false /* do not wait for ourself */);
cifs_done_oplock_break(cinode);
}

Expand Down
25 changes: 23 additions & 2 deletions fs/cifs/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&pCifsInode->flags);

queue_work(cifsoplockd_wq,
&netfile->oplock_break);
cifs_queue_oplock_break(netfile);
netfile->oplock_break_cancelled = false;

spin_unlock(&tcon->open_file_lock);
Expand Down Expand Up @@ -607,6 +606,28 @@ void cifs_put_writer(struct cifsInodeInfo *cinode)
spin_unlock(&cinode->writers_lock);
}

/**
* cifs_queue_oplock_break - queue the oplock break handler for cfile
*
* This function is called from the demultiplex thread when it
* receives an oplock break for @cfile.
*
* Assumes the tcon->open_file_lock is held.
* Assumes cfile->file_info_lock is NOT held.
*/
void cifs_queue_oplock_break(struct cifsFileInfo *cfile)
{
/*
* Bump the handle refcount now while we hold the
* open_file_lock to enforce the validity of it for the oplock
* break handler. The matching put is done at the end of the
* handler.
*/
cifsFileInfo_get(cfile);

queue_work(cifsoplockd_wq, &cfile->oplock_break);
}

void cifs_done_oplock_break(struct cifsInodeInfo *cinode)
{
clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
Expand Down
6 changes: 3 additions & 3 deletions fs/cifs/smb2misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp,
clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags);

queue_work(cifsoplockd_wq, &cfile->oplock_break);
cifs_queue_oplock_break(cfile);
kfree(lw);
return true;
}
Expand Down Expand Up @@ -712,8 +712,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags);
spin_unlock(&cfile->file_info_lock);
queue_work(cifsoplockd_wq,
&cfile->oplock_break);

cifs_queue_oplock_break(cfile);

spin_unlock(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock);
Expand Down
2 changes: 2 additions & 0 deletions fs/cifs/smb2ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -2389,6 +2389,8 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,

rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_iov,
&resp_buftype);
if (!rc)
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
if (!rc || !err_iov.iov_base) {
rc = -ENOENT;
goto free_path;
Expand Down
11 changes: 7 additions & 4 deletions fs/cifs/smb2pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -832,8 +832,11 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
/* ops set to 3.0 by default for default so update */
ses->server->ops = &smb21_operations;
} else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID))
ses->server->vals = &smb21_values;
} else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
ses->server->ops = &smb311_operations;
ses->server->vals = &smb311_values;
}
} else if (le16_to_cpu(rsp->DialectRevision) !=
ses->server->vals->protocol_id) {
/* if requested single dialect ensure returned dialect matched */
Expand Down Expand Up @@ -3448,8 +3451,6 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
rqst.rq_nvec = 1;

rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);

rsp = (struct smb2_read_rsp *)rsp_iov.iov_base;

if (rc) {
Expand All @@ -3471,6 +3472,8 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
io_parms->tcon->tid, ses->Suid,
io_parms->offset, io_parms->length);

cifs_small_buf_release(req);

*nbytes = le32_to_cpu(rsp->DataLength);
if ((*nbytes > CIFS_MAX_MSGSIZE) ||
(*nbytes > io_parms->length)) {
Expand Down Expand Up @@ -3769,7 +3772,6 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,

rc = cifs_send_recv(xid, io_parms->tcon->ses, &rqst,
&resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_write_rsp *)rsp_iov.iov_base;

if (rc) {
Expand All @@ -3787,6 +3789,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
io_parms->offset, *nbytes);
}

cifs_small_buf_release(req);
free_rsp_buf(resp_buftype, rsp);
return rc;
}
Expand Down

0 comments on commit e53f31b

Please sign in to comment.