Commit e53f31bf authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '5.1-rc5-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6

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
parents fe5cdef2 b98749ca
...@@ -1333,6 +1333,7 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file) ...@@ -1333,6 +1333,7 @@ cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
} }
struct cifsFileInfo *cifsFileInfo_get(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); void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
#define CIFS_CACHE_READ_FLG 1 #define CIFS_CACHE_READ_FLG 1
...@@ -1855,6 +1856,7 @@ GLOBAL_EXTERN spinlock_t gidsidlock; ...@@ -1855,6 +1856,7 @@ GLOBAL_EXTERN spinlock_t gidsidlock;
#endif /* CONFIG_CIFS_ACL */ #endif /* CONFIG_CIFS_ACL */
void cifs_oplock_break(struct work_struct *work); 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 const struct slow_work_ops cifs_oplock_break_ops;
extern struct workqueue_struct *cifsiod_wq; extern struct workqueue_struct *cifsiod_wq;
......
...@@ -360,12 +360,30 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file) ...@@ -360,12 +360,30 @@ cifsFileInfo_get(struct cifsFileInfo *cifs_file)
return cifs_file; return cifs_file;
} }
/* /**
* Release a reference on the file private data. This may involve closing * cifsFileInfo_put - release a reference of file priv data
* the filehandle out on the server. Must be called without holding *
* tcon->open_file_lock and cifs_file->file_info_lock. * Always potentially wait for oplock handler. See _cifsFileInfo_put().
*/ */
void cifsFileInfo_put(struct cifsFileInfo *cifs_file) 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 inode *inode = d_inode(cifs_file->dentry);
struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink); struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
...@@ -414,7 +432,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file) ...@@ -414,7 +432,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
spin_unlock(&tcon->open_file_lock); 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) { if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
...@@ -4603,6 +4622,7 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -4603,6 +4622,7 @@ void cifs_oplock_break(struct work_struct *work)
cinode); cinode);
cifs_dbg(FYI, "Oplock release rc = %d\n", rc); cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
} }
_cifsFileInfo_put(cfile, false /* do not wait for ourself */);
cifs_done_oplock_break(cinode); cifs_done_oplock_break(cinode);
} }
......
...@@ -501,8 +501,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv) ...@@ -501,8 +501,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&pCifsInode->flags); &pCifsInode->flags);
queue_work(cifsoplockd_wq, cifs_queue_oplock_break(netfile);
&netfile->oplock_break);
netfile->oplock_break_cancelled = false; netfile->oplock_break_cancelled = false;
spin_unlock(&tcon->open_file_lock); spin_unlock(&tcon->open_file_lock);
...@@ -607,6 +606,28 @@ void cifs_put_writer(struct cifsInodeInfo *cinode) ...@@ -607,6 +606,28 @@ void cifs_put_writer(struct cifsInodeInfo *cinode)
spin_unlock(&cinode->writers_lock); 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) void cifs_done_oplock_break(struct cifsInodeInfo *cinode)
{ {
clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags);
......
...@@ -555,7 +555,7 @@ smb2_tcon_has_lease(struct cifs_tcon *tcon, struct smb2_lease_break *rsp, ...@@ -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, clear_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags); &cinode->flags);
queue_work(cifsoplockd_wq, &cfile->oplock_break); cifs_queue_oplock_break(cfile);
kfree(lw); kfree(lw);
return true; return true;
} }
...@@ -712,8 +712,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server) ...@@ -712,8 +712,8 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
&cinode->flags); &cinode->flags);
spin_unlock(&cfile->file_info_lock); 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(&tcon->open_file_lock);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
......
...@@ -2389,6 +2389,8 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -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, rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, &err_iov,
&resp_buftype); &resp_buftype);
if (!rc)
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
if (!rc || !err_iov.iov_base) { if (!rc || !err_iov.iov_base) {
rc = -ENOENT; rc = -ENOENT;
goto free_path; goto free_path;
......
...@@ -832,8 +832,11 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) ...@@ -832,8 +832,11 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) { } else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
/* ops set to 3.0 by default for default so update */ /* ops set to 3.0 by default for default so update */
ses->server->ops = &smb21_operations; 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->ops = &smb311_operations;
ses->server->vals = &smb311_values;
}
} else if (le16_to_cpu(rsp->DialectRevision) != } else if (le16_to_cpu(rsp->DialectRevision) !=
ses->server->vals->protocol_id) { ses->server->vals->protocol_id) {
/* if requested single dialect ensure returned dialect matched */ /* if requested single dialect ensure returned dialect matched */
...@@ -3448,8 +3451,6 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, ...@@ -3448,8 +3451,6 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
rqst.rq_nvec = 1; rqst.rq_nvec = 1;
rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, &rsp_iov); 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; rsp = (struct smb2_read_rsp *)rsp_iov.iov_base;
if (rc) { if (rc) {
...@@ -3471,6 +3472,8 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms, ...@@ -3471,6 +3472,8 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
io_parms->tcon->tid, ses->Suid, io_parms->tcon->tid, ses->Suid,
io_parms->offset, io_parms->length); io_parms->offset, io_parms->length);
cifs_small_buf_release(req);
*nbytes = le32_to_cpu(rsp->DataLength); *nbytes = le32_to_cpu(rsp->DataLength);
if ((*nbytes > CIFS_MAX_MSGSIZE) || if ((*nbytes > CIFS_MAX_MSGSIZE) ||
(*nbytes > io_parms->length)) { (*nbytes > io_parms->length)) {
...@@ -3769,7 +3772,6 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, ...@@ -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, rc = cifs_send_recv(xid, io_parms->tcon->ses, &rqst,
&resp_buftype, flags, &rsp_iov); &resp_buftype, flags, &rsp_iov);
cifs_small_buf_release(req);
rsp = (struct smb2_write_rsp *)rsp_iov.iov_base; rsp = (struct smb2_write_rsp *)rsp_iov.iov_base;
if (rc) { if (rc) {
...@@ -3787,6 +3789,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms, ...@@ -3787,6 +3789,7 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
io_parms->offset, *nbytes); io_parms->offset, *nbytes);
} }
cifs_small_buf_release(req);
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment