Commit 8bb14ca1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '5.13-rc3-smb3' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs fixes from Steve French:
 "Seven smb3 fixes: one for stable, three others fix problems found in
  testing handle leases, and a compounded request fix"

* tag '5.13-rc3-smb3' of git://git.samba.org/sfrench/cifs-2.6:
  Fix KASAN identified use-after-free issue.
  Defer close only when lease is enabled.
  Fix kernel oops when CONFIG_DEBUG_ATOMIC_SLEEP is enabled.
  cifs: Fix inconsistent indenting
  cifs: fix memory leak in smb2_copychunk_range
  SMB3: incorrect file id in requests compounded with open
  cifs: remove deadstore in cifs_close_all_deferred_files()
parents e8085a07 9687c85d
...@@ -1257,8 +1257,7 @@ struct cifsFileInfo { ...@@ -1257,8 +1257,7 @@ struct cifsFileInfo {
struct work_struct oplock_break; /* work for oplock breaks */ struct work_struct oplock_break; /* work for oplock breaks */
struct work_struct put; /* work for the final part of _put */ struct work_struct put; /* work for the final part of _put */
struct delayed_work deferred; struct delayed_work deferred;
bool oplock_break_received; /* Flag to indicate oplock break */ bool deferred_close_scheduled; /* Flag to indicate close is scheduled */
bool deferred_scheduled;
}; };
struct cifs_io_parms { struct cifs_io_parms {
...@@ -1418,6 +1417,7 @@ struct cifsInodeInfo { ...@@ -1418,6 +1417,7 @@ struct cifsInodeInfo {
struct inode vfs_inode; struct inode vfs_inode;
struct list_head deferred_closes; /* list of deferred closes */ struct list_head deferred_closes; /* list of deferred closes */
spinlock_t deferred_lock; /* protection on deferred list */ spinlock_t deferred_lock; /* protection on deferred list */
bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */
}; };
static inline struct cifsInodeInfo * static inline struct cifsInodeInfo *
......
...@@ -323,8 +323,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, ...@@ -323,8 +323,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
cfile->dentry = dget(dentry); cfile->dentry = dget(dentry);
cfile->f_flags = file->f_flags; cfile->f_flags = file->f_flags;
cfile->invalidHandle = false; cfile->invalidHandle = false;
cfile->oplock_break_received = false; cfile->deferred_close_scheduled = false;
cfile->deferred_scheduled = false;
cfile->tlink = cifs_get_tlink(tlink); cfile->tlink = cifs_get_tlink(tlink);
INIT_WORK(&cfile->oplock_break, cifs_oplock_break); INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
INIT_WORK(&cfile->put, cifsFileInfo_put_work); INIT_WORK(&cfile->put, cifsFileInfo_put_work);
...@@ -574,21 +573,18 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -574,21 +573,18 @@ int cifs_open(struct inode *inode, struct file *file)
file->f_op = &cifs_file_direct_ops; file->f_op = &cifs_file_direct_ops;
} }
spin_lock(&CIFS_I(inode)->deferred_lock);
/* Get the cached handle as SMB2 close is deferred */ /* Get the cached handle as SMB2 close is deferred */
rc = cifs_get_readable_path(tcon, full_path, &cfile); rc = cifs_get_readable_path(tcon, full_path, &cfile);
if (rc == 0) { if (rc == 0) {
if (file->f_flags == cfile->f_flags) { if (file->f_flags == cfile->f_flags) {
file->private_data = cfile; file->private_data = cfile;
spin_lock(&CIFS_I(inode)->deferred_lock);
cifs_del_deferred_close(cfile); cifs_del_deferred_close(cfile);
spin_unlock(&CIFS_I(inode)->deferred_lock); spin_unlock(&CIFS_I(inode)->deferred_lock);
goto out; goto out;
} else { } else {
spin_unlock(&CIFS_I(inode)->deferred_lock);
_cifsFileInfo_put(cfile, true, false); _cifsFileInfo_put(cfile, true, false);
} }
} else {
spin_unlock(&CIFS_I(inode)->deferred_lock);
} }
if (server->oplocks) if (server->oplocks)
...@@ -878,12 +874,8 @@ void smb2_deferred_work_close(struct work_struct *work) ...@@ -878,12 +874,8 @@ void smb2_deferred_work_close(struct work_struct *work)
struct cifsFileInfo, deferred.work); struct cifsFileInfo, deferred.work);
spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
if (!cfile->deferred_scheduled) {
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
return;
}
cifs_del_deferred_close(cfile); cifs_del_deferred_close(cfile);
cfile->deferred_scheduled = false; cfile->deferred_close_scheduled = false;
spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock); spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
_cifsFileInfo_put(cfile, true, false); _cifsFileInfo_put(cfile, true, false);
} }
...@@ -900,19 +892,26 @@ int cifs_close(struct inode *inode, struct file *file) ...@@ -900,19 +892,26 @@ int cifs_close(struct inode *inode, struct file *file)
file->private_data = NULL; file->private_data = NULL;
dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL); dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL);
if ((cinode->oplock == CIFS_CACHE_RHW_FLG) && if ((cinode->oplock == CIFS_CACHE_RHW_FLG) &&
cinode->lease_granted &&
dclose) { dclose) {
if (test_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags)) if (test_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags))
inode->i_ctime = inode->i_mtime = current_time(inode); inode->i_ctime = inode->i_mtime = current_time(inode);
spin_lock(&cinode->deferred_lock); spin_lock(&cinode->deferred_lock);
cifs_add_deferred_close(cfile, dclose); cifs_add_deferred_close(cfile, dclose);
if (cfile->deferred_scheduled) { if (cfile->deferred_close_scheduled &&
mod_delayed_work(deferredclose_wq, delayed_work_pending(&cfile->deferred)) {
&cfile->deferred, cifs_sb->ctx->acregmax); /*
* If there is no pending work, mod_delayed_work queues new work.
* So, Increase the ref count to avoid use-after-free.
*/
if (!mod_delayed_work(deferredclose_wq,
&cfile->deferred, cifs_sb->ctx->acregmax))
cifsFileInfo_get(cfile);
} else { } else {
/* Deferred close for files */ /* Deferred close for files */
queue_delayed_work(deferredclose_wq, queue_delayed_work(deferredclose_wq,
&cfile->deferred, cifs_sb->ctx->acregmax); &cfile->deferred, cifs_sb->ctx->acregmax);
cfile->deferred_scheduled = true; cfile->deferred_close_scheduled = true;
spin_unlock(&cinode->deferred_lock); spin_unlock(&cinode->deferred_lock);
return 0; return 0;
} }
...@@ -2020,8 +2019,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, ...@@ -2020,8 +2019,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))
continue; continue;
if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) { if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) {
if ((!open_file->invalidHandle) && if ((!open_file->invalidHandle)) {
(!open_file->oplock_break_received)) {
/* found a good file */ /* found a good file */
/* lock it so it will not be closed on us */ /* lock it so it will not be closed on us */
cifsFileInfo_get(open_file); cifsFileInfo_get(open_file);
...@@ -4874,14 +4872,20 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -4874,14 +4872,20 @@ void cifs_oplock_break(struct work_struct *work)
} }
/* /*
* When oplock break is received and there are no active * When oplock break is received and there are no active
* file handles but cached, then set the flag oplock_break_received. * file handles but cached, then schedule deferred close immediately.
* So, new open will not use cached handle. * So, new open will not use cached handle.
*/ */
spin_lock(&CIFS_I(inode)->deferred_lock); spin_lock(&CIFS_I(inode)->deferred_lock);
is_deferred = cifs_is_deferred_close(cfile, &dclose); is_deferred = cifs_is_deferred_close(cfile, &dclose);
if (is_deferred && cfile->deferred_scheduled) { if (is_deferred &&
cfile->oplock_break_received = true; cfile->deferred_close_scheduled &&
mod_delayed_work(deferredclose_wq, &cfile->deferred, 0); delayed_work_pending(&cfile->deferred)) {
/*
* If there is no pending work, mod_delayed_work queues new work.
* So, Increase the ref count to avoid use-after-free.
*/
if (!mod_delayed_work(deferredclose_wq, &cfile->deferred, 0))
cifsFileInfo_get(cfile);
} }
spin_unlock(&CIFS_I(inode)->deferred_lock); spin_unlock(&CIFS_I(inode)->deferred_lock);
_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false); _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
......
...@@ -672,6 +672,11 @@ cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink, ...@@ -672,6 +672,11 @@ cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
spin_unlock(&tlink_tcon(open->tlink)->open_file_lock); spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
} }
/*
* Critical section which runs after acquiring deferred_lock.
* As there is no reference count on cifs_deferred_close, pdclose
* should not be used outside deferred_lock.
*/
bool bool
cifs_is_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close **pdclose) cifs_is_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close **pdclose)
{ {
...@@ -688,6 +693,9 @@ cifs_is_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close ** ...@@ -688,6 +693,9 @@ cifs_is_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close **
return false; return false;
} }
/*
* Critical section which runs after acquiring deferred_lock.
*/
void void
cifs_add_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close *dclose) cifs_add_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close *dclose)
{ {
...@@ -707,6 +715,9 @@ cifs_add_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close * ...@@ -707,6 +715,9 @@ cifs_add_deferred_close(struct cifsFileInfo *cfile, struct cifs_deferred_close *
list_add_tail(&dclose->dlist, &CIFS_I(d_inode(cfile->dentry))->deferred_closes); list_add_tail(&dclose->dlist, &CIFS_I(d_inode(cfile->dentry))->deferred_closes);
} }
/*
* Critical section which runs after acquiring deferred_lock.
*/
void void
cifs_del_deferred_close(struct cifsFileInfo *cfile) cifs_del_deferred_close(struct cifsFileInfo *cfile)
{ {
...@@ -738,15 +749,19 @@ void ...@@ -738,15 +749,19 @@ void
cifs_close_all_deferred_files(struct cifs_tcon *tcon) cifs_close_all_deferred_files(struct cifs_tcon *tcon)
{ {
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct cifsInodeInfo *cinode;
struct list_head *tmp; struct list_head *tmp;
spin_lock(&tcon->open_file_lock); spin_lock(&tcon->open_file_lock);
list_for_each(tmp, &tcon->openFileList) { list_for_each(tmp, &tcon->openFileList) {
cfile = list_entry(tmp, struct cifsFileInfo, tlist); cfile = list_entry(tmp, struct cifsFileInfo, tlist);
cinode = CIFS_I(d_inode(cfile->dentry)); if (delayed_work_pending(&cfile->deferred)) {
if (delayed_work_pending(&cfile->deferred)) /*
mod_delayed_work(deferredclose_wq, &cfile->deferred, 0); * If there is no pending work, mod_delayed_work queues new work.
* So, Increase the ref count to avoid use-after-free.
*/
if (!mod_delayed_work(deferredclose_wq, &cfile->deferred, 0))
cifsFileInfo_get(cfile);
}
} }
spin_unlock(&tcon->open_file_lock); spin_unlock(&tcon->open_file_lock);
} }
......
...@@ -1861,6 +1861,8 @@ smb2_copychunk_range(const unsigned int xid, ...@@ -1861,6 +1861,8 @@ smb2_copychunk_range(const unsigned int xid,
cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk)); cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk));
/* Request server copy to target from src identified by key */ /* Request server copy to target from src identified by key */
kfree(retbuf);
retbuf = NULL;
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
true /* is_fsctl */, (char *)pcchunk, true /* is_fsctl */, (char *)pcchunk,
...@@ -3981,6 +3983,7 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, ...@@ -3981,6 +3983,7 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
unsigned int epoch, bool *purge_cache) unsigned int epoch, bool *purge_cache)
{ {
oplock &= 0xFF; oplock &= 0xFF;
cinode->lease_granted = false;
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
return; return;
if (oplock == SMB2_OPLOCK_LEVEL_BATCH) { if (oplock == SMB2_OPLOCK_LEVEL_BATCH) {
...@@ -4007,6 +4010,7 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, ...@@ -4007,6 +4010,7 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
unsigned int new_oplock = 0; unsigned int new_oplock = 0;
oplock &= 0xFF; oplock &= 0xFF;
cinode->lease_granted = true;
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
return; return;
......
...@@ -3900,10 +3900,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len, ...@@ -3900,10 +3900,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
* Related requests use info from previous read request * Related requests use info from previous read request
* in chain. * in chain.
*/ */
shdr->SessionId = 0xFFFFFFFF; shdr->SessionId = 0xFFFFFFFFFFFFFFFF;
shdr->TreeId = 0xFFFFFFFF; shdr->TreeId = 0xFFFFFFFF;
req->PersistentFileId = 0xFFFFFFFF; req->PersistentFileId = 0xFFFFFFFFFFFFFFFF;
req->VolatileFileId = 0xFFFFFFFF; req->VolatileFileId = 0xFFFFFFFFFFFFFFFF;
} }
} }
if (remaining_bytes > io_parms->length) if (remaining_bytes > io_parms->length)
......
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