Skip to content

Commit 3dc9c43

Browse files
Paulo AlcantaraSteve French
authored andcommitted
cifs: protect access of TCP_Server_Info::{origin,leaf}_fullpath
Protect access of TCP_Server_Info::{origin,leaf}_fullpath when matching DFS connections, and get rid of TCP_Server_Info::current_fullpath while we're at it. Cc: [email protected] # v6.2+ Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent ee20d7c commit 3dc9c43

File tree

5 files changed

+43
-20
lines changed

5 files changed

+43
-20
lines changed

fs/cifs/cifsglob.h

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -736,17 +736,23 @@ struct TCP_Server_Info {
736736
#endif
737737
struct mutex refpath_lock; /* protects leaf_fullpath */
738738
/*
739-
* Canonical DFS full paths that were used to chase referrals in mount and reconnect.
739+
* origin_fullpath: Canonical copy of smb3_fs_context::source.
740+
* It is used for matching existing DFS tcons.
740741
*
741-
* origin_fullpath: first or original referral path
742-
* leaf_fullpath: last referral path (might be changed due to nested links in reconnect)
742+
* leaf_fullpath: Canonical DFS referral path related to this
743+
* connection.
744+
* It is used in DFS cache refresher, reconnect and may
745+
* change due to nested DFS links.
743746
*
744-
* current_fullpath: pointer to either origin_fullpath or leaf_fullpath
745-
* NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect()
747+
* Both protected by @refpath_lock and @srv_lock. The @refpath_lock is
748+
* mosly used for not requiring a copy of @leaf_fullpath when getting
749+
* cached or new DFS referrals (which might also sleep during I/O).
750+
* While @srv_lock is held for making string and NULL comparions against
751+
* both fields as in mount(2) and cache refresh.
746752
*
747-
* format: \\HOST\SHARE\[OPTIONAL PATH]
753+
* format: \\HOST\SHARE[\OPTIONAL PATH]
748754
*/
749-
char *origin_fullpath, *leaf_fullpath, *current_fullpath;
755+
char *origin_fullpath, *leaf_fullpath;
750756
};
751757

752758
static inline bool is_smb1(struct TCP_Server_Info *server)

fs/cifs/connect.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,6 @@ static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_
454454
static int reconnect_dfs_server(struct TCP_Server_Info *server)
455455
{
456456
int rc = 0;
457-
const char *refpath = server->current_fullpath + 1;
458457
struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
459458
struct dfs_cache_tgt_iterator *target_hint = NULL;
460459
int num_targets = 0;
@@ -467,8 +466,10 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
467466
* through /proc/fs/cifs/dfscache or the target list is empty due to server settings after
468467
* refreshing the referral, so, in this case, default it to 1.
469468
*/
470-
if (!dfs_cache_noreq_find(refpath, NULL, &tl))
469+
mutex_lock(&server->refpath_lock);
470+
if (!dfs_cache_noreq_find(server->leaf_fullpath + 1, NULL, &tl))
471471
num_targets = dfs_cache_get_nr_tgts(&tl);
472+
mutex_unlock(&server->refpath_lock);
472473
if (!num_targets)
473474
num_targets = 1;
474475

@@ -512,7 +513,9 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
512513
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
513514
} while (server->tcpStatus == CifsNeedReconnect);
514515

515-
dfs_cache_noreq_update_tgthint(refpath, target_hint);
516+
mutex_lock(&server->refpath_lock);
517+
dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, target_hint);
518+
mutex_unlock(&server->refpath_lock);
516519
dfs_cache_free_tgts(&tl);
517520

518521
/* Need to set up echo worker again once connection has been established */
@@ -1582,7 +1585,6 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
15821585
rc = -ENOMEM;
15831586
goto out_err;
15841587
}
1585-
tcp_ses->current_fullpath = tcp_ses->leaf_fullpath;
15861588
}
15871589

15881590
if (ctx->nosharesock)

fs/cifs/dfs.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,12 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
248248
tcon = mnt_ctx->tcon;
249249

250250
mutex_lock(&server->refpath_lock);
251+
spin_lock(&server->srv_lock);
251252
if (!server->origin_fullpath) {
252253
server->origin_fullpath = origin_fullpath;
253-
server->current_fullpath = server->leaf_fullpath;
254254
origin_fullpath = NULL;
255255
}
256+
spin_unlock(&server->srv_lock);
256257
mutex_unlock(&server->refpath_lock);
257258

258259
if (list_empty(&tcon->dfs_ses_list)) {
@@ -342,10 +343,11 @@ static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb
342343
rc = PTR_ERR(npath);
343344
} else {
344345
mutex_lock(&server->refpath_lock);
346+
spin_lock(&server->srv_lock);
345347
kfree(server->leaf_fullpath);
346348
server->leaf_fullpath = npath;
349+
spin_unlock(&server->srv_lock);
347350
mutex_unlock(&server->refpath_lock);
348-
server->current_fullpath = server->leaf_fullpath;
349351
}
350352
return rc;
351353
}
@@ -450,7 +452,7 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
450452
share = prefix = NULL;
451453

452454
/* Check if share matches with tcp ses */
453-
rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix);
455+
rc = dfs_cache_get_tgt_share(server->leaf_fullpath + 1, tit, &share, &prefix);
454456
if (rc) {
455457
cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc);
456458
break;
@@ -464,7 +466,7 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
464466
continue;
465467
}
466468

467-
dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit);
469+
dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, tit);
468470
tree_connect_ipc(xid, tree, cifs_sb, tcon);
469471

470472
scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
@@ -582,8 +584,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
582584
cifs_sb = CIFS_SB(sb);
583585

584586
/* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
585-
if (!server->current_fullpath ||
586-
dfs_cache_noreq_find(server->current_fullpath + 1, &ref, &tl)) {
587+
if (!server->leaf_fullpath ||
588+
dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) {
587589
rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls);
588590
goto out;
589591
}

fs/cifs/dfs.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,12 @@ static inline char *dfs_get_automount_devname(struct dentry *dentry, void *page)
4343
size_t len;
4444
char *s;
4545

46-
if (unlikely(!server->origin_fullpath))
46+
spin_lock(&server->srv_lock);
47+
if (unlikely(!server->origin_fullpath)) {
48+
spin_unlock(&server->srv_lock);
4749
return ERR_PTR(-EREMOTE);
50+
}
51+
spin_unlock(&server->srv_lock);
4852

4953
s = dentry_path_raw(dentry, page, PATH_MAX);
5054
if (IS_ERR(s))
@@ -53,13 +57,18 @@ static inline char *dfs_get_automount_devname(struct dentry *dentry, void *page)
5357
if (!s[1])
5458
s++;
5559

60+
spin_lock(&server->srv_lock);
5661
len = strlen(server->origin_fullpath);
57-
if (s < (char *)page + len)
62+
if (s < (char *)page + len) {
63+
spin_unlock(&server->srv_lock);
5864
return ERR_PTR(-ENAMETOOLONG);
65+
}
5966

6067
s -= len;
6168
memcpy(s, server->origin_fullpath, len);
69+
spin_unlock(&server->srv_lock);
6270
convert_delimiter(s, '/');
71+
6372
return s;
6473
}
6574

fs/cifs/dfs_cache.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1278,8 +1278,12 @@ static void refresh_cache_worker(struct work_struct *work)
12781278

12791279
spin_lock(&cifs_tcp_ses_lock);
12801280
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
1281-
if (!server->leaf_fullpath)
1281+
spin_lock(&server->srv_lock);
1282+
if (!server->leaf_fullpath) {
1283+
spin_unlock(&server->srv_lock);
12821284
continue;
1285+
}
1286+
spin_unlock(&server->srv_lock);
12831287

12841288
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
12851289
if (ses->tcon_ipc) {

0 commit comments

Comments
 (0)