Skip to content

Commit 119c289

Browse files
committed
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
2 parents 6c6e47d + e0e5040 commit 119c289

22 files changed

+369
-178
lines changed

fs/smb/client/cached_dir.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ smb2_close_cached_fid(struct kref *ref)
417417
{
418418
struct cached_fid *cfid = container_of(ref, struct cached_fid,
419419
refcount);
420+
int rc;
420421

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

432433
if (cfid->is_open) {
433-
SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
434+
rc = SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
434435
cfid->fid.volatile_fid);
435-
atomic_dec(&cfid->tcon->num_remote_opens);
436+
if (rc != -EBUSY && rc != -EAGAIN)
437+
atomic_dec(&cfid->tcon->num_remote_opens);
436438
}
437439

438440
free_cached_dir(cfid);

fs/smb/client/cifs_debug.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
250250
spin_lock(&cifs_tcp_ses_lock);
251251
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
252252
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
253+
if (cifs_ses_exiting(ses))
254+
continue;
253255
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
254256
spin_lock(&tcon->open_file_lock);
255257
list_for_each_entry(cfile, &tcon->openFileList, tlist) {
@@ -676,6 +678,8 @@ static ssize_t cifs_stats_proc_write(struct file *file,
676678
}
677679
#endif /* CONFIG_CIFS_STATS2 */
678680
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
681+
if (cifs_ses_exiting(ses))
682+
continue;
679683
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
680684
atomic_set(&tcon->num_smbs_sent, 0);
681685
spin_lock(&tcon->stat_lock);
@@ -755,6 +759,8 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
755759
}
756760
#endif /* STATS2 */
757761
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
762+
if (cifs_ses_exiting(ses))
763+
continue;
758764
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
759765
i++;
760766
seq_printf(m, "\n%d) %s", i, tcon->tree_name);

fs/smb/client/cifsfs.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ struct workqueue_struct *decrypt_wq;
156156
struct workqueue_struct *fileinfo_put_wq;
157157
struct workqueue_struct *cifsoplockd_wq;
158158
struct workqueue_struct *deferredclose_wq;
159+
struct workqueue_struct *serverclose_wq;
159160
__u32 cifs_lock_secret;
160161

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

1892+
serverclose_wq = alloc_workqueue("serverclose",
1893+
WQ_FREEZABLE|WQ_MEM_RECLAIM, 0);
1894+
if (!serverclose_wq) {
1895+
rc = -ENOMEM;
1896+
goto out_destroy_serverclose_wq;
1897+
}
1898+
18911899
rc = cifs_init_inodecache();
18921900
if (rc)
18931901
goto out_destroy_deferredclose_wq;
@@ -1962,6 +1970,8 @@ init_cifs(void)
19621970
destroy_workqueue(decrypt_wq);
19631971
out_destroy_cifsiod_wq:
19641972
destroy_workqueue(cifsiod_wq);
1973+
out_destroy_serverclose_wq:
1974+
destroy_workqueue(serverclose_wq);
19651975
out_clean_proc:
19661976
cifs_proc_clean();
19671977
return rc;
@@ -1991,6 +2001,7 @@ exit_cifs(void)
19912001
destroy_workqueue(cifsoplockd_wq);
19922002
destroy_workqueue(decrypt_wq);
19932003
destroy_workqueue(fileinfo_put_wq);
2004+
destroy_workqueue(serverclose_wq);
19942005
destroy_workqueue(cifsiod_wq);
19952006
cifs_proc_clean();
19962007
}

fs/smb/client/cifsglob.h

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -442,10 +442,10 @@ struct smb_version_operations {
442442
/* set fid protocol-specific info */
443443
void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
444444
/* close a file */
445-
void (*close)(const unsigned int, struct cifs_tcon *,
445+
int (*close)(const unsigned int, struct cifs_tcon *,
446446
struct cifs_fid *);
447447
/* close a file, returning file attributes and timestamps */
448-
void (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon,
448+
int (*close_getattr)(const unsigned int xid, struct cifs_tcon *tcon,
449449
struct cifsFileInfo *pfile_info);
450450
/* send a flush request to the server */
451451
int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *);
@@ -1281,7 +1281,6 @@ struct cifs_tcon {
12811281
struct cached_fids *cfids;
12821282
/* BB add field for back pointer to sb struct(s)? */
12831283
#ifdef CONFIG_CIFS_DFS_UPCALL
1284-
struct list_head dfs_ses_list;
12851284
struct delayed_work dfs_cache_work;
12861285
#endif
12871286
struct delayed_work query_interfaces; /* query interfaces workqueue job */
@@ -1440,6 +1439,7 @@ struct cifsFileInfo {
14401439
bool swapfile:1;
14411440
bool oplock_break_cancelled:1;
14421441
bool status_file_deleted:1; /* file has been deleted */
1442+
bool offload:1; /* offload final part of _put to a wq */
14431443
unsigned int oplock_epoch; /* epoch from the lease break */
14441444
__u32 oplock_level; /* oplock/lease level from the lease break */
14451445
int count;
@@ -1448,6 +1448,7 @@ struct cifsFileInfo {
14481448
struct cifs_search_info srch_inf;
14491449
struct work_struct oplock_break; /* work for oplock breaks */
14501450
struct work_struct put; /* work for the final part of _put */
1451+
struct work_struct serverclose; /* work for serverclose */
14511452
struct delayed_work deferred;
14521453
bool deferred_close_scheduled; /* Flag to indicate close is scheduled */
14531454
char *symlink_target;
@@ -1804,7 +1805,6 @@ struct cifs_mount_ctx {
18041805
struct TCP_Server_Info *server;
18051806
struct cifs_ses *ses;
18061807
struct cifs_tcon *tcon;
1807-
struct list_head dfs_ses_list;
18081808
};
18091809

18101810
static inline void __free_dfs_info_param(struct dfs_info3_param *param)
@@ -2105,6 +2105,7 @@ extern struct workqueue_struct *decrypt_wq;
21052105
extern struct workqueue_struct *fileinfo_put_wq;
21062106
extern struct workqueue_struct *cifsoplockd_wq;
21072107
extern struct workqueue_struct *deferredclose_wq;
2108+
extern struct workqueue_struct *serverclose_wq;
21082109
extern __u32 cifs_lock_secret;
21092110

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

2328+
static inline bool cifs_ses_exiting(struct cifs_ses *ses)
2329+
{
2330+
bool ret;
2331+
2332+
spin_lock(&ses->ses_lock);
2333+
ret = ses->ses_status == SES_EXITING;
2334+
spin_unlock(&ses->ses_lock);
2335+
return ret;
2336+
}
2337+
23272338
#endif /* _CIFS_GLOB_H */

fs/smb/client/cifsproto.h

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -725,31 +725,31 @@ struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
725725
void cifs_put_tcon_super(struct super_block *sb);
726726
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
727727

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

733-
__cifs_put_smb_ses(ses);
734-
if (rses)
735-
__cifs_put_smb_ses(rses);
733+
do {
734+
next = ses->dfs_root_ses;
735+
__cifs_put_smb_ses(ses);
736+
} while ((ses = next));
736737
}
737738

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

750-
ses->ses_count++;
751-
if (ses->dfs_root_ses)
752-
ses->dfs_root_ses->ses_count++;
751+
for (; ses; ses = ses->dfs_root_ses)
752+
ses->ses_count++;
753753
}
754754

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

fs/smb/client/cifssmb.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5854,10 +5854,8 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
58545854
parm_data->list.EA_flags = 0;
58555855
/* we checked above that name len is less than 255 */
58565856
parm_data->list.name_len = (__u8)name_len;
5857-
/* EA names are always ASCII */
5858-
if (ea_name)
5859-
strncpy(parm_data->list.name, ea_name, name_len);
5860-
parm_data->list.name[name_len] = '\0';
5857+
/* EA names are always ASCII and NUL-terminated */
5858+
strscpy(parm_data->list.name, ea_name ?: "", name_len + 1);
58615859
parm_data->list.value_len = cpu_to_le16(ea_value_len);
58625860
/* caller ensures that ea_value_len is less than 64K but
58635861
we need to ensure that it fits within the smb */

0 commit comments

Comments
 (0)