Commit 1d22968f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'v7.0-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

 - Fix three potential double free vulnerabilities

 - Fix data corruption due to racy lease checks

 - Enforce SMB1 signing verification checks

 - Fix invalid mount option parsing

 - Remove unneeded tracepoint

 - Various minor error code corrections

 - Minor cleanup

* tag 'v7.0-rc-part2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb: client: terminate session upon failed client required signing
  cifs: some missing initializations on replay
  cifs: remove unnecessary tracing after put tcon
  cifs: update internal module version number
  smb: client: fix data corruption due to racy lease checks
  smb/client: move NT_STATUS_MORE_ENTRIES
  smb/client: rename to NT_ERROR_INVALID_DATATYPE
  smb/client: rename to NT_STATUS_SOME_NOT_MAPPED
  smb/client: map NT_STATUS_PRIVILEGE_NOT_HELD
  smb/client: map NT_STATUS_MORE_PROCESSING_REQUIRED
  smb/client: map NT_STATUS_BUFFER_OVERFLOW
  smb/client: map NT_STATUS_NOTIFY_ENUM_DIR
  cifs: SMB1 split: Remove duplicate include of cifs_debug.h
  smb: client: fix regression with mount options parsing
parents e81dd54f dc96f01d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -147,6 +147,6 @@ extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */

/* when changing internal version - update following two lines at same time */
#define SMB3_PRODUCT_BUILD 58
#define CIFS_VERSION   "2.58"
#define SMB3_PRODUCT_BUILD 59
#define CIFS_VERSION   "2.59"
#endif				/* _CIFSFS_H */
+30 −6
Original line number Diff line number Diff line
@@ -515,8 +515,10 @@ struct smb_version_operations {
	/* check for STATUS_NETWORK_SESSION_EXPIRED */
	bool (*is_session_expired)(char *);
	/* send oplock break response */
	int (*oplock_response)(struct cifs_tcon *tcon, __u64 persistent_fid, __u64 volatile_fid,
			__u16 net_fid, struct cifsInodeInfo *cifs_inode);
	int (*oplock_response)(struct cifs_tcon *tcon, __u64 persistent_fid,
			       __u64 volatile_fid, __u16 net_fid,
			       struct cifsInodeInfo *cifs_inode,
			       unsigned int oplock);
	/* query remote filesystem */
	int (*queryfs)(const unsigned int, struct cifs_tcon *,
		       const char *, struct cifs_sb_info *, struct kstatfs *);
@@ -1531,10 +1533,6 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
#define CIFS_CACHE_RW_FLG	(CIFS_CACHE_READ_FLG | CIFS_CACHE_WRITE_FLG)
#define CIFS_CACHE_RHW_FLG	(CIFS_CACHE_RW_FLG | CIFS_CACHE_HANDLE_FLG)

#define CIFS_CACHE_READ(cinode) ((cinode->oplock & CIFS_CACHE_READ_FLG) || (CIFS_SB(cinode->netfs.inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE))
#define CIFS_CACHE_HANDLE(cinode) (cinode->oplock & CIFS_CACHE_HANDLE_FLG)
#define CIFS_CACHE_WRITE(cinode) ((cinode->oplock & CIFS_CACHE_WRITE_FLG) || (CIFS_SB(cinode->netfs.inode.i_sb)->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE))

/*
 * One of these for each file inode
 */
@@ -2312,4 +2310,30 @@ static inline void cifs_requeue_server_reconn(struct TCP_Server_Info *server)
	queue_delayed_work(cifsiod_wq, &server->reconnect, delay * HZ);
}

static inline bool __cifs_cache_state_check(struct cifsInodeInfo *cinode,
					    unsigned int oplock_flags,
					    unsigned int sb_flags)
{
	struct cifs_sb_info *cifs_sb = CIFS_SB(cinode->netfs.inode.i_sb);
	unsigned int oplock = READ_ONCE(cinode->oplock);
	unsigned int sflags = cifs_sb->mnt_cifs_flags;

	return (oplock & oplock_flags) || (sflags & sb_flags);
}

#define CIFS_CACHE_READ(cinode) \
	__cifs_cache_state_check(cinode, CIFS_CACHE_READ_FLG, \
				 CIFS_MOUNT_RO_CACHE)
#define CIFS_CACHE_HANDLE(cinode) \
	__cifs_cache_state_check(cinode, CIFS_CACHE_HANDLE_FLG, 0)
#define CIFS_CACHE_WRITE(cinode) \
	__cifs_cache_state_check(cinode, CIFS_CACHE_WRITE_FLG, \
				 CIFS_MOUNT_RW_CACHE)

static inline void cifs_reset_oplock(struct cifsInodeInfo *cinode)
{
	scoped_guard(spinlock, &cinode->open_file_lock)
		WRITE_ONCE(cinode->oplock, 0);
}

#endif	/* _CIFS_GLOB_H */
+36 −21
Original line number Diff line number Diff line
@@ -731,14 +731,14 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
		oplock = fid->pending_open->oplock;
	list_del(&fid->pending_open->olist);

	fid->purge_cache = false;
	server->ops->set_fid(cfile, fid, oplock);

	list_add(&cfile->tlist, &tcon->openFileList);
	atomic_inc(&tcon->num_local_opens);

	/* if readable file instance put first in list*/
	spin_lock(&cinode->open_file_lock);
	fid->purge_cache = false;
	server->ops->set_fid(cfile, fid, oplock);

	if (file->f_mode & FMODE_READ)
		list_add(&cfile->flist, &cinode->openFileList);
	else
@@ -1410,6 +1410,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
		oplock = 0;
	}

	scoped_guard(spinlock, &cinode->open_file_lock)
		server->ops->set_fid(cfile, &cfile->fid, oplock);
	if (oparms.reconnect)
		cifs_relock_file(cfile);
@@ -1437,11 +1438,11 @@ smb2_can_defer_close(struct inode *inode, struct cifs_deferred_close *dclose)
{
	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
	struct cifsInodeInfo *cinode = CIFS_I(inode);
	unsigned int oplock = READ_ONCE(cinode->oplock);

	return (cifs_sb->ctx->closetimeo && cinode->lease_granted && dclose &&
			(cinode->oplock == CIFS_CACHE_RHW_FLG ||
			 cinode->oplock == CIFS_CACHE_RH_FLG) &&
			!test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags));
	return cifs_sb->ctx->closetimeo && cinode->lease_granted && dclose &&
		(oplock == CIFS_CACHE_RHW_FLG || oplock == CIFS_CACHE_RH_FLG) &&
		!test_bit(CIFS_INO_CLOSE_ON_LOCK, &cinode->flags);

}

@@ -2371,7 +2372,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
			cifs_zap_mapping(inode);
			cifs_dbg(FYI, "Set no oplock for inode=%p due to mand locks\n",
				 inode);
			CIFS_I(inode)->oplock = 0;
			cifs_reset_oplock(CIFS_I(inode));
		}

		rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
@@ -2930,7 +2931,7 @@ cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)
		cifs_zap_mapping(inode);
		cifs_dbg(FYI, "Set Oplock/Lease to NONE for inode=%p after write\n",
			 inode);
		cinode->oplock = 0;
		cifs_reset_oplock(cinode);
	}
out:
	cifs_put_writer(cinode);
@@ -2966,7 +2967,7 @@ ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
			cifs_dbg(FYI,
				 "Set no oplock for inode=%p after a write operation\n",
				 inode);
			cinode->oplock = 0;
			cifs_reset_oplock(cinode);
		}
		return written;
	}
@@ -3154,9 +3155,11 @@ void cifs_oplock_break(struct work_struct *work)
	struct super_block *sb = inode->i_sb;
	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
	struct cifsInodeInfo *cinode = CIFS_I(inode);
	bool cache_read, cache_write, cache_handle;
	struct cifs_tcon *tcon;
	struct TCP_Server_Info *server;
	struct tcon_link *tlink;
	unsigned int oplock;
	int rc = 0;
	bool purge_cache = false, oplock_break_cancelled;
	__u64 persistent_fid, volatile_fid;
@@ -3177,29 +3180,40 @@ void cifs_oplock_break(struct work_struct *work)
	tcon = tlink_tcon(tlink);
	server = tcon->ses->server;

	scoped_guard(spinlock, &cinode->open_file_lock) {
		unsigned int sbflags = cifs_sb->mnt_cifs_flags;

		server->ops->downgrade_oplock(server, cinode, cfile->oplock_level,
					      cfile->oplock_epoch, &purge_cache);
		oplock = READ_ONCE(cinode->oplock);
		cache_read = (oplock & CIFS_CACHE_READ_FLG) ||
			(sbflags & CIFS_MOUNT_RO_CACHE);
		cache_write = (oplock & CIFS_CACHE_WRITE_FLG) ||
			(sbflags & CIFS_MOUNT_RW_CACHE);
		cache_handle = oplock & CIFS_CACHE_HANDLE_FLG;
	}

	if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) &&
						cifs_has_mand_locks(cinode)) {
	if (!cache_write && cache_read && cifs_has_mand_locks(cinode)) {
		cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n",
			 inode);
		cinode->oplock = 0;
		cifs_reset_oplock(cinode);
		oplock = 0;
		cache_read = cache_write = cache_handle = false;
	}

	if (S_ISREG(inode->i_mode)) {
		if (CIFS_CACHE_READ(cinode))
		if (cache_read)
			break_lease(inode, O_RDONLY);
		else
			break_lease(inode, O_WRONLY);
		rc = filemap_fdatawrite(inode->i_mapping);
		if (!CIFS_CACHE_READ(cinode) || purge_cache) {
		if (!cache_read || purge_cache) {
			rc = filemap_fdatawait(inode->i_mapping);
			mapping_set_error(inode->i_mapping, rc);
			cifs_zap_mapping(inode);
		}
		cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc);
		if (CIFS_CACHE_WRITE(cinode))
		if (cache_write)
			goto oplock_break_ack;
	}

@@ -3214,7 +3228,7 @@ void cifs_oplock_break(struct work_struct *work)
	 * So, new open will not use cached handle.
	 */

	if (!CIFS_CACHE_HANDLE(cinode) && !list_empty(&cinode->deferred_closes))
	if (!cache_handle && !list_empty(&cinode->deferred_closes))
		cifs_close_deferred_file(cinode);

	persistent_fid = cfile->fid.persistent_fid;
@@ -3232,7 +3246,8 @@ void cifs_oplock_break(struct work_struct *work)
	if (!oplock_break_cancelled && !list_empty(&cinode->openFileList)) {
		spin_unlock(&cinode->open_file_lock);
		rc = server->ops->oplock_response(tcon, persistent_fid,
						  volatile_fid, net_fid, cinode);
						  volatile_fid, net_fid,
						  cinode, oplock);
		cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
	} else
		spin_unlock(&cinode->open_file_lock);
+1 −3
Original line number Diff line number Diff line
@@ -825,9 +825,7 @@ static int smb3_fs_context_parse_monolithic(struct fs_context *fc,
		if (ret < 0)
			break;
	}
	ret = smb3_handle_conflicting_options(fc);

	return ret;
	return ret ?: smb3_handle_conflicting_options(fc);
}

/*
+2 −1
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
const struct nt_err_code_struct nt_errs[] = {
	{"NT_STATUS_OK", NT_STATUS_OK},
	{"NT_STATUS_PENDING", NT_STATUS_PENDING},
	{"NT_STATUS_NOTIFY_ENUM_DIR", NT_STATUS_NOTIFY_ENUM_DIR},
	{"NT_STATUS_MEDIA_CHANGED", NT_STATUS_MEDIA_CHANGED},
	{"NT_STATUS_END_OF_MEDIA", NT_STATUS_END_OF_MEDIA},
	{"NT_STATUS_MEDIA_CHECK", NT_STATUS_MEDIA_CHECK},
@@ -694,7 +695,7 @@ const struct nt_err_code_struct nt_errs[] = {
	{"NT_STATUS_NETWORK_SESSION_EXPIRED", NT_STATUS_NETWORK_SESSION_EXPIRED},
	{"NT_STATUS_NO_MORE_ENTRIES", NT_STATUS_NO_MORE_ENTRIES},
	{"NT_STATUS_MORE_ENTRIES", NT_STATUS_MORE_ENTRIES},
	{"NT_STATUS_SOME_UNMAPPED", NT_STATUS_SOME_UNMAPPED},
	{"NT_STATUS_SOME_NOT_MAPPED", NT_STATUS_SOME_NOT_MAPPED},
	{"NT_STATUS_NO_SUCH_JOB", NT_STATUS_NO_SUCH_JOB},
	{"NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP",
	 NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP},
Loading