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

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
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1257,8 +1257,7 @@ struct cifsFileInfo {
	struct work_struct oplock_break; /* work for oplock breaks */
	struct work_struct put; /* work for the final part of _put */
	struct delayed_work deferred;
	bool oplock_break_received; /* Flag to indicate oplock break */
	bool deferred_scheduled;
	bool deferred_close_scheduled; /* Flag to indicate close is scheduled */
};

struct cifs_io_parms {
@@ -1418,6 +1417,7 @@ struct cifsInodeInfo {
	struct inode vfs_inode;
	struct list_head deferred_closes; /* list of deferred closes */
	spinlock_t deferred_lock; /* protection on deferred list */
	bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */
};

static inline struct cifsInodeInfo *
+25 −21
Original line number Diff line number Diff line
@@ -323,8 +323,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
	cfile->dentry = dget(dentry);
	cfile->f_flags = file->f_flags;
	cfile->invalidHandle = false;
	cfile->oplock_break_received = false;
	cfile->deferred_scheduled = false;
	cfile->deferred_close_scheduled = false;
	cfile->tlink = cifs_get_tlink(tlink);
	INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
	INIT_WORK(&cfile->put, cifsFileInfo_put_work);
@@ -574,21 +573,18 @@ int cifs_open(struct inode *inode, struct file *file)
			file->f_op = &cifs_file_direct_ops;
	}

	spin_lock(&CIFS_I(inode)->deferred_lock);
	/* Get the cached handle as SMB2 close is deferred */
	rc = cifs_get_readable_path(tcon, full_path, &cfile);
	if (rc == 0) {
		if (file->f_flags == cfile->f_flags) {
			file->private_data = cfile;
			spin_lock(&CIFS_I(inode)->deferred_lock);
			cifs_del_deferred_close(cfile);
			spin_unlock(&CIFS_I(inode)->deferred_lock);
			goto out;
		} else {
			spin_unlock(&CIFS_I(inode)->deferred_lock);
			_cifsFileInfo_put(cfile, true, false);
		}
	} else {
		spin_unlock(&CIFS_I(inode)->deferred_lock);
	}

	if (server->oplocks)
@@ -878,12 +874,8 @@ void smb2_deferred_work_close(struct work_struct *work)
			struct cifsFileInfo, deferred.work);

	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);
	cfile->deferred_scheduled = false;
	cfile->deferred_close_scheduled = false;
	spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
	_cifsFileInfo_put(cfile, true, false);
}
@@ -900,19 +892,26 @@ int cifs_close(struct inode *inode, struct file *file)
		file->private_data = NULL;
		dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL);
		if ((cinode->oplock == CIFS_CACHE_RHW_FLG) &&
		    cinode->lease_granted &&
		    dclose) {
			if (test_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags))
				inode->i_ctime = inode->i_mtime = current_time(inode);
			spin_lock(&cinode->deferred_lock);
			cifs_add_deferred_close(cfile, dclose);
			if (cfile->deferred_scheduled) {
				mod_delayed_work(deferredclose_wq,
						&cfile->deferred, cifs_sb->ctx->acregmax);
			if (cfile->deferred_close_scheduled &&
			    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, cifs_sb->ctx->acregmax))
					cifsFileInfo_get(cfile);
			} else {
				/* Deferred close for files */
				queue_delayed_work(deferredclose_wq,
						&cfile->deferred, cifs_sb->ctx->acregmax);
				cfile->deferred_scheduled = true;
				cfile->deferred_close_scheduled = true;
				spin_unlock(&cinode->deferred_lock);
				return 0;
			}
@@ -2020,8 +2019,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
		if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))
			continue;
		if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) {
			if ((!open_file->invalidHandle) &&
				(!open_file->oplock_break_received)) {
			if ((!open_file->invalidHandle)) {
				/* found a good file */
				/* lock it so it will not be closed on us */
				cifsFileInfo_get(open_file);
@@ -4874,14 +4872,20 @@ void cifs_oplock_break(struct work_struct *work)
	}
	/*
	 * 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.
	 */
	spin_lock(&CIFS_I(inode)->deferred_lock);
	is_deferred = cifs_is_deferred_close(cfile, &dclose);
	if (is_deferred && cfile->deferred_scheduled) {
		cfile->oplock_break_received = true;
		mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
	if (is_deferred &&
	    cfile->deferred_close_scheduled &&
	    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);
	_cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
+19 −4
Original line number Diff line number Diff line
@@ -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);
}

/*
 * 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
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 **
	return false;
}

/*
 * Critical section which runs after acquiring deferred_lock.
 */
void
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 *
	list_add_tail(&dclose->dlist, &CIFS_I(d_inode(cfile->dentry))->deferred_closes);
}

/*
 * Critical section which runs after acquiring deferred_lock.
 */
void
cifs_del_deferred_close(struct cifsFileInfo *cfile)
{
@@ -738,15 +749,19 @@ void
cifs_close_all_deferred_files(struct cifs_tcon *tcon)
{
	struct cifsFileInfo *cfile;
	struct cifsInodeInfo *cinode;
	struct list_head *tmp;

	spin_lock(&tcon->open_file_lock);
	list_for_each(tmp, &tcon->openFileList) {
		cfile = list_entry(tmp, struct cifsFileInfo, tlist);
		cinode = CIFS_I(d_inode(cfile->dentry));
		if (delayed_work_pending(&cfile->deferred))
			mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
		if (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(&tcon->open_file_lock);
}
+4 −0
Original line number Diff line number Diff line
@@ -1861,6 +1861,8 @@ smb2_copychunk_range(const unsigned int xid,
			cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk));

		/* Request server copy to target from src identified by key */
		kfree(retbuf);
		retbuf = NULL;
		rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
			trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
			true /* is_fsctl */, (char *)pcchunk,
@@ -3981,6 +3983,7 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
		      unsigned int epoch, bool *purge_cache)
{
	oplock &= 0xFF;
	cinode->lease_granted = false;
	if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
		return;
	if (oplock == SMB2_OPLOCK_LEVEL_BATCH) {
@@ -4007,6 +4010,7 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
	unsigned int new_oplock = 0;

	oplock &= 0xFF;
	cinode->lease_granted = true;
	if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
		return;

+3 −3
Original line number Diff line number Diff line
@@ -3900,10 +3900,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
			 * Related requests use info from previous read request
			 * in chain.
			 */
			shdr->SessionId = 0xFFFFFFFF;
			shdr->SessionId = 0xFFFFFFFFFFFFFFFF;
			shdr->TreeId = 0xFFFFFFFF;
			req->PersistentFileId = 0xFFFFFFFF;
			req->VolatileFileId = 0xFFFFFFFF;
			req->PersistentFileId = 0xFFFFFFFFFFFFFFFF;
			req->VolatileFileId = 0xFFFFFFFFFFFFFFFF;
		}
	}
	if (remaining_bytes > io_parms->length)
+1 −1

File changed.

Contains only whitespace changes.

+1 −1

File changed.

Contains only whitespace changes.

Loading