Commit 119c2894 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag '6.9-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

 - fix to retry close to avoid potential handle leaks when server
   returns EBUSY

 - DFS fixes including a fix for potential use after free

 - fscache fix

 - minor strncpy cleanup

 - reconnect race fix

 - deal with various possible UAF race conditions tearing sessions down

* tag '6.9-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb: client: fix potential UAF in cifs_signal_cifsd_for_reconnect()
  smb: client: fix potential UAF in smb2_is_network_name_deleted()
  smb: client: fix potential UAF in is_valid_oplock_break()
  smb: client: fix potential UAF in smb2_is_valid_oplock_break()
  smb: client: fix potential UAF in smb2_is_valid_lease_break()
  smb: client: fix potential UAF in cifs_stats_proc_show()
  smb: client: fix potential UAF in cifs_stats_proc_write()
  smb: client: fix potential UAF in cifs_dump_full_key()
  smb: client: fix potential UAF in cifs_debug_files_proc_show()
  smb3: retrying on failed server close
  smb: client: serialise cifs_construct_tcon() with cifs_mount_mutex
  smb: client: handle DFS tcons in cifs_construct_tcon()
  smb: client: refresh referral without acquiring refpath_lock
  smb: client: guarantee refcounted children from parent session
  cifs: Fix caching to try to do open O_WRONLY as rdwr on server
  smb: client: fix UAF in smb2_reconnect_server()
  smb: client: replace deprecated strncpy with strscpy
parents 6c6e47d6 e0e50401
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -417,6 +417,7 @@ smb2_close_cached_fid(struct kref *ref)
{
	struct cached_fid *cfid = container_of(ref, struct cached_fid,
					       refcount);
	int rc;

	spin_lock(&cfid->cfids->cfid_list_lock);
	if (cfid->on_list) {
@@ -430,8 +431,9 @@ smb2_close_cached_fid(struct kref *ref)
	cfid->dentry = NULL;

	if (cfid->is_open) {
		SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
		rc = SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
			   cfid->fid.volatile_fid);
		if (rc != -EBUSY && rc != -EAGAIN)
			atomic_dec(&cfid->tcon->num_remote_opens);
	}

+6 −0
Original line number Diff line number Diff line
@@ -250,6 +250,8 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
	spin_lock(&cifs_tcp_ses_lock);
	list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
		list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
			if (cifs_ses_exiting(ses))
				continue;
			list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
				spin_lock(&tcon->open_file_lock);
				list_for_each_entry(cfile, &tcon->openFileList, tlist) {
@@ -676,6 +678,8 @@ static ssize_t cifs_stats_proc_write(struct file *file,
			}
#endif /* CONFIG_CIFS_STATS2 */
			list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
				if (cifs_ses_exiting(ses))
					continue;
				list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
					atomic_set(&tcon->num_smbs_sent, 0);
					spin_lock(&tcon->stat_lock);
@@ -755,6 +759,8 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
			}
#endif /* STATS2 */
		list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
			if (cifs_ses_exiting(ses))
				continue;
			list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
				i++;
				seq_printf(m, "\n%d) %s", i, tcon->tree_name);
+11 −0
Original line number Diff line number Diff line
@@ -156,6 +156,7 @@ struct workqueue_struct *decrypt_wq;
struct workqueue_struct	*fileinfo_put_wq;
struct workqueue_struct	*cifsoplockd_wq;
struct workqueue_struct	*deferredclose_wq;
struct workqueue_struct	*serverclose_wq;
__u32 cifs_lock_secret;

/*
@@ -1888,6 +1889,13 @@ init_cifs(void)
		goto out_destroy_cifsoplockd_wq;
	}

	serverclose_wq = alloc_workqueue("serverclose",
					   WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
	if (!serverclose_wq) {
		rc = -ENOMEM;
		goto out_destroy_serverclose_wq;
	}

	rc = cifs_init_inodecache();
	if (rc)
		goto out_destroy_deferredclose_wq;
@@ -1962,6 +1970,8 @@ init_cifs(void)
	destroy_workqueue(decrypt_wq);
out_destroy_cifsiod_wq:
	destroy_workqueue(cifsiod_wq);
out_destroy_serverclose_wq:
	destroy_workqueue(serverclose_wq);
out_clean_proc:
	cifs_proc_clean();
	return rc;
@@ -1991,6 +2001,7 @@ exit_cifs(void)
	destroy_workqueue(cifsoplockd_wq);
	destroy_workqueue(decrypt_wq);
	destroy_workqueue(fileinfo_put_wq);
	destroy_workqueue(serverclose_wq);
	destroy_workqueue(cifsiod_wq);
	cifs_proc_clean();
}
+15 −4
Original line number Diff line number Diff line
@@ -442,10 +442,10 @@ struct smb_version_operations {
	/* set fid protocol-specific info */
	void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
	/* close a file */
	void (*close)(const unsigned int, struct cifs_tcon *,
	int (*close)(const unsigned int, struct cifs_tcon *,
		      struct cifs_fid *);
	/* close a file, returning file attributes and timestamps */
	void (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon,
	int (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon,
		      struct cifsFileInfo *pfile_info);
	/* send a flush request to the server */
	int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *);
@@ -1281,7 +1281,6 @@ struct cifs_tcon {
	struct cached_fids *cfids;
	/* BB add field for back pointer to sb struct(s)? */
#ifdef CONFIG_CIFS_DFS_UPCALL
	struct list_head dfs_ses_list;
	struct delayed_work dfs_cache_work;
#endif
	struct delayed_work	query_interfaces; /* query interfaces workqueue job */
@@ -1440,6 +1439,7 @@ struct cifsFileInfo {
	bool swapfile:1;
	bool oplock_break_cancelled:1;
	bool status_file_deleted:1; /* file has been deleted */
	bool offload:1; /* offload final part of _put to a wq */
	unsigned int oplock_epoch; /* epoch from the lease break */
	__u32 oplock_level; /* oplock/lease level from the lease break */
	int count;
@@ -1448,6 +1448,7 @@ struct cifsFileInfo {
	struct cifs_search_info srch_inf;
	struct work_struct oplock_break; /* work for oplock breaks */
	struct work_struct put; /* work for the final part of _put */
	struct work_struct serverclose; /* work for serverclose */
	struct delayed_work deferred;
	bool deferred_close_scheduled; /* Flag to indicate close is scheduled */
	char *symlink_target;
@@ -1804,7 +1805,6 @@ struct cifs_mount_ctx {
	struct TCP_Server_Info *server;
	struct cifs_ses *ses;
	struct cifs_tcon *tcon;
	struct list_head dfs_ses_list;
};

static inline void __free_dfs_info_param(struct dfs_info3_param *param)
@@ -2105,6 +2105,7 @@ extern struct workqueue_struct *decrypt_wq;
extern struct workqueue_struct *fileinfo_put_wq;
extern struct workqueue_struct *cifsoplockd_wq;
extern struct workqueue_struct *deferredclose_wq;
extern struct workqueue_struct *serverclose_wq;
extern __u32 cifs_lock_secret;

extern mempool_t *cifs_sm_req_poolp;
@@ -2324,4 +2325,14 @@ struct smb2_compound_vars {
	struct kvec ea_iov;
};

static inline bool cifs_ses_exiting(struct cifs_ses *ses)
{
	bool ret;

	spin_lock(&ses->ses_lock);
	ret = ses->ses_status == SES_EXITING;
	spin_unlock(&ses->ses_lock);
	return ret;
}

#endif	/* _CIFS_GLOB_H */
+10 −10
Original line number Diff line number Diff line
@@ -725,31 +725,31 @@ struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
void cifs_put_tcon_super(struct super_block *sb);
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);

/* Put references of @ses and @ses->dfs_root_ses */
/* Put references of @ses and its children */
static inline void cifs_put_smb_ses(struct cifs_ses *ses)
{
	struct cifs_ses *rses = ses->dfs_root_ses;
	struct cifs_ses *next;

	do {
		next = ses->dfs_root_ses;
		__cifs_put_smb_ses(ses);
	if (rses)
		__cifs_put_smb_ses(rses);
	} while ((ses = next));
}

/* Get an active reference of @ses and @ses->dfs_root_ses.
/* Get an active reference of @ses and its children.
 *
 * NOTE: make sure to call this function when incrementing reference count of
 * @ses to ensure that any DFS root session attached to it (@ses->dfs_root_ses)
 * will also get its reference count incremented.
 *
 * cifs_put_smb_ses() will put both references, so call it when you're done.
 * cifs_put_smb_ses() will put all references, so call it when you're done.
 */
static inline void cifs_smb_ses_inc_refcount(struct cifs_ses *ses)
{
	lockdep_assert_held(&cifs_tcp_ses_lock);

	for (; ses; ses = ses->dfs_root_ses)
		ses->ses_count++;
	if (ses->dfs_root_ses)
		ses->dfs_root_ses->ses_count++;
}

static inline bool dfs_src_pathname_equal(const char *s1, const char *s2)
Loading