Skip to content

Commit 396935d

Browse files
Paulo AlcantaraSteve French
authored andcommitted
cifs: fix use-after-free bug in refresh_cache_worker()
The UAF bug occurred because we were putting DFS root sessions in cifs_umount() while DFS cache refresher was being executed. Make DFS root sessions have same lifetime as DFS tcons so we can avoid the use-after-free bug is DFS cache refresher and other places that require IPCs to get new DFS referrals on. Also, get rid of mount group handling in DFS cache as we no longer need it. This fixes below use-after-free bug catched by KASAN [ 379.946955] BUG: KASAN: use-after-free in __refresh_tcon.isra.0+0x10b/0xc10 [cifs] [ 379.947642] Read of size 8 at addr ffff888018f57030 by task kworker/u4:3/56 [ 379.948096] [ 379.948208] CPU: 0 PID: 56 Comm: kworker/u4:3 Not tainted 6.2.0-rc7-lku #23 [ 379.948661] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.0-0-gd239552-rebuilt.opensuse.org 04/01/2014 [ 379.949368] Workqueue: cifs-dfscache refresh_cache_worker [cifs] [ 379.949942] Call Trace: [ 379.950113] <TASK> [ 379.950260] dump_stack_lvl+0x50/0x67 [ 379.950510] print_report+0x16a/0x48e [ 379.950759] ? __virt_addr_valid+0xd8/0x160 [ 379.951040] ? __phys_addr+0x41/0x80 [ 379.951285] kasan_report+0xdb/0x110 [ 379.951533] ? __refresh_tcon.isra.0+0x10b/0xc10 [cifs] [ 379.952056] ? __refresh_tcon.isra.0+0x10b/0xc10 [cifs] [ 379.952585] __refresh_tcon.isra.0+0x10b/0xc10 [cifs] [ 379.953096] ? __pfx___refresh_tcon.isra.0+0x10/0x10 [cifs] [ 379.953637] ? __pfx___mutex_lock+0x10/0x10 [ 379.953915] ? lock_release+0xb6/0x720 [ 379.954167] ? __pfx_lock_acquire+0x10/0x10 [ 379.954443] ? refresh_cache_worker+0x34e/0x6d0 [cifs] [ 379.954960] ? __pfx_wb_workfn+0x10/0x10 [ 379.955239] refresh_cache_worker+0x4ad/0x6d0 [cifs] [ 379.955755] ? __pfx_refresh_cache_worker+0x10/0x10 [cifs] [ 379.956323] ? __pfx_lock_acquired+0x10/0x10 [ 379.956615] ? read_word_at_a_time+0xe/0x20 [ 379.956898] ? lockdep_hardirqs_on_prepare+0x12/0x220 [ 379.957235] process_one_work+0x535/0x990 [ 379.957509] ? __pfx_process_one_work+0x10/0x10 [ 379.957812] ? lock_acquired+0xb7/0x5f0 [ 379.958069] ? __list_add_valid+0x37/0xd0 [ 379.958341] ? __list_add_valid+0x37/0xd0 [ 379.958611] worker_thread+0x8e/0x630 [ 379.958861] ? __pfx_worker_thread+0x10/0x10 [ 379.959148] kthread+0x17d/0x1b0 [ 379.959369] ? __pfx_kthread+0x10/0x10 [ 379.959630] ret_from_fork+0x2c/0x50 [ 379.959879] </TASK> Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Cc: [email protected] # 6.2 Signed-off-by: Steve French <[email protected]>
1 parent b56bce5 commit 396935d

File tree

8 files changed

+67
-164
lines changed

8 files changed

+67
-164
lines changed

fs/cifs/cifs_fs_sb.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ struct cifs_sb_info {
6161
/* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */
6262
char *prepath;
6363

64-
/* randomly generated 128-bit number for indexing dfs mount groups in referral cache */
65-
uuid_t dfs_mount_id;
6664
/*
6765
* Indicate whether serverino option was turned off later
6866
* (cifs_autodisable_serverino) in order to match new mounts.

fs/cifs/cifsglob.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,7 @@ struct cifs_tcon {
12331233
/* BB add field for back pointer to sb struct(s)? */
12341234
#ifdef CONFIG_CIFS_DFS_UPCALL
12351235
struct list_head ulist; /* cache update list */
1236+
struct list_head dfs_ses_list;
12361237
#endif
12371238
struct delayed_work query_interfaces; /* query interfaces workqueue job */
12381239
};
@@ -1749,8 +1750,8 @@ struct cifs_mount_ctx {
17491750
struct TCP_Server_Info *server;
17501751
struct cifs_ses *ses;
17511752
struct cifs_tcon *tcon;
1752-
uuid_t mount_id;
17531753
char *origin_fullpath, *leaf_fullpath;
1754+
struct list_head dfs_ses_list;
17541755
};
17551756

17561757
static inline void free_dfs_info_param(struct dfs_info3_param *param)

fs/cifs/connect.c

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3408,7 +3408,8 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
34083408
bool isdfs;
34093409
int rc;
34103410

3411-
uuid_gen(&mnt_ctx.mount_id);
3411+
INIT_LIST_HEAD(&mnt_ctx.dfs_ses_list);
3412+
34123413
rc = dfs_mount_share(&mnt_ctx, &isdfs);
34133414
if (rc)
34143415
goto error;
@@ -3428,7 +3429,6 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
34283429
kfree(cifs_sb->prepath);
34293430
cifs_sb->prepath = ctx->prepath;
34303431
ctx->prepath = NULL;
3431-
uuid_copy(&cifs_sb->dfs_mount_id, &mnt_ctx.mount_id);
34323432

34333433
out:
34343434
cifs_try_adding_channels(cifs_sb, mnt_ctx.ses);
@@ -3440,7 +3440,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
34403440
return rc;
34413441

34423442
error:
3443-
dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id);
3443+
dfs_put_root_smb_sessions(&mnt_ctx.dfs_ses_list);
34443444
kfree(mnt_ctx.origin_fullpath);
34453445
kfree(mnt_ctx.leaf_fullpath);
34463446
cifs_mount_put_conns(&mnt_ctx);
@@ -3638,9 +3638,6 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
36383638
spin_unlock(&cifs_sb->tlink_tree_lock);
36393639

36403640
kfree(cifs_sb->prepath);
3641-
#ifdef CONFIG_CIFS_DFS_UPCALL
3642-
dfs_cache_put_refsrv_sessions(&cifs_sb->dfs_mount_id);
3643-
#endif
36443641
call_rcu(&cifs_sb->rcu, delayed_free);
36453642
}
36463643

fs/cifs/dfs.c

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -99,26 +99,36 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
9999
return rc;
100100
}
101101

102-
static void set_root_ses(struct cifs_mount_ctx *mnt_ctx)
102+
static int get_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
103103
{
104104
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
105+
struct dfs_root_ses *root_ses;
105106
struct cifs_ses *ses = mnt_ctx->ses;
106107

107108
if (ses) {
109+
root_ses = kmalloc(sizeof(*root_ses), GFP_KERNEL);
110+
if (!root_ses)
111+
return -ENOMEM;
112+
113+
INIT_LIST_HEAD(&root_ses->list);
114+
108115
spin_lock(&cifs_tcp_ses_lock);
109116
ses->ses_count++;
110117
spin_unlock(&cifs_tcp_ses_lock);
111-
dfs_cache_add_refsrv_session(&mnt_ctx->mount_id, ses);
118+
root_ses->ses = ses;
119+
list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list);
112120
}
113-
ctx->dfs_root_ses = mnt_ctx->ses;
121+
ctx->dfs_root_ses = ses;
122+
return 0;
114123
}
115124

116125
static int get_dfs_conn(struct cifs_mount_ctx *mnt_ctx, const char *ref_path, const char *full_path,
117126
const struct dfs_cache_tgt_iterator *tit)
118127
{
119128
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
120129
struct dfs_info3_param ref = {};
121-
int rc;
130+
bool is_refsrv = false;
131+
int rc, rc2;
122132

123133
rc = dfs_cache_get_tgt_referral(ref_path + 1, tit, &ref);
124134
if (rc)
@@ -133,8 +143,7 @@ static int get_dfs_conn(struct cifs_mount_ctx *mnt_ctx, const char *ref_path, co
133143
if (rc)
134144
goto out;
135145

136-
if (ref.flags & DFSREF_REFERRAL_SERVER)
137-
set_root_ses(mnt_ctx);
146+
is_refsrv = !!(ref.flags & DFSREF_REFERRAL_SERVER);
138147

139148
rc = -EREMOTE;
140149
if (ref.flags & DFSREF_STORAGE_SERVER) {
@@ -143,13 +152,17 @@ static int get_dfs_conn(struct cifs_mount_ctx *mnt_ctx, const char *ref_path, co
143152
goto out;
144153

145154
/* some servers may not advertise referral capability under ref.flags */
146-
if (!(ref.flags & DFSREF_REFERRAL_SERVER) &&
147-
is_tcon_dfs(mnt_ctx->tcon))
148-
set_root_ses(mnt_ctx);
155+
is_refsrv |= is_tcon_dfs(mnt_ctx->tcon);
149156

150157
rc = cifs_is_path_remote(mnt_ctx);
151158
}
152159

160+
if (rc == -EREMOTE && is_refsrv) {
161+
rc2 = get_root_smb_session(mnt_ctx);
162+
if (rc2)
163+
rc = rc2;
164+
}
165+
153166
out:
154167
free_dfs_info_param(&ref);
155168
return rc;
@@ -162,6 +175,7 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
162175
char *ref_path = NULL, *full_path = NULL;
163176
struct dfs_cache_tgt_iterator *tit;
164177
struct TCP_Server_Info *server;
178+
struct cifs_tcon *tcon;
165179
char *origin_fullpath = NULL;
166180
int num_links = 0;
167181
int rc;
@@ -231,12 +245,22 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
231245

232246
if (!rc) {
233247
server = mnt_ctx->server;
248+
tcon = mnt_ctx->tcon;
234249

235250
mutex_lock(&server->refpath_lock);
236-
server->origin_fullpath = origin_fullpath;
237-
server->current_fullpath = server->leaf_fullpath;
251+
if (!server->origin_fullpath) {
252+
server->origin_fullpath = origin_fullpath;
253+
server->current_fullpath = server->leaf_fullpath;
254+
origin_fullpath = NULL;
255+
}
238256
mutex_unlock(&server->refpath_lock);
239-
origin_fullpath = NULL;
257+
258+
if (list_empty(&tcon->dfs_ses_list)) {
259+
list_replace_init(&mnt_ctx->dfs_ses_list,
260+
&tcon->dfs_ses_list);
261+
} else {
262+
dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list);
263+
}
240264
}
241265

242266
out:
@@ -277,7 +301,9 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
277301
}
278302

279303
*isdfs = true;
280-
set_root_ses(mnt_ctx);
304+
rc = get_root_smb_session(mnt_ctx);
305+
if (rc)
306+
return rc;
281307

282308
return __dfs_mount_share(mnt_ctx);
283309
}

fs/cifs/dfs.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
#include "fs_context.h"
1111
#include "cifs_unicode.h"
1212

13+
struct dfs_root_ses {
14+
struct list_head list;
15+
struct cifs_ses *ses;
16+
};
17+
1318
int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref,
1419
struct smb3_fs_context *ctx);
1520
int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs);
@@ -44,4 +49,15 @@ static inline char *dfs_get_automount_devname(struct dentry *dentry, void *page)
4449
true);
4550
}
4651

52+
static inline void dfs_put_root_smb_sessions(struct list_head *head)
53+
{
54+
struct dfs_root_ses *root, *tmp;
55+
56+
list_for_each_entry_safe(root, tmp, head, list) {
57+
list_del_init(&root->list);
58+
cifs_put_smb_ses(root->ses);
59+
kfree(root);
60+
}
61+
}
62+
4763
#endif /* _CIFS_DFS_H */

fs/cifs/dfs_cache.c

Lines changed: 0 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,6 @@ struct cache_entry {
4949
struct cache_dfs_tgt *tgthint;
5050
};
5151

52-
/* List of referral server sessions per dfs mount */
53-
struct mount_group {
54-
struct list_head list;
55-
uuid_t id;
56-
struct cifs_ses *sessions[CACHE_MAX_ENTRIES];
57-
int num_sessions;
58-
spinlock_t lock;
59-
struct list_head refresh_list;
60-
struct kref refcount;
61-
};
62-
6352
static struct kmem_cache *cache_slab __read_mostly;
6453
static struct workqueue_struct *dfscache_wq __read_mostly;
6554

@@ -76,85 +65,10 @@ static atomic_t cache_count;
7665
static struct hlist_head cache_htable[CACHE_HTABLE_SIZE];
7766
static DECLARE_RWSEM(htable_rw_lock);
7867

79-
static LIST_HEAD(mount_group_list);
80-
static DEFINE_MUTEX(mount_group_list_lock);
81-
8268
static void refresh_cache_worker(struct work_struct *work);
8369

8470
static DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker);
8571

86-
static void __mount_group_release(struct mount_group *mg)
87-
{
88-
int i;
89-
90-
for (i = 0; i < mg->num_sessions; i++)
91-
cifs_put_smb_ses(mg->sessions[i]);
92-
kfree(mg);
93-
}
94-
95-
static void mount_group_release(struct kref *kref)
96-
{
97-
struct mount_group *mg = container_of(kref, struct mount_group, refcount);
98-
99-
mutex_lock(&mount_group_list_lock);
100-
list_del(&mg->list);
101-
mutex_unlock(&mount_group_list_lock);
102-
__mount_group_release(mg);
103-
}
104-
105-
static struct mount_group *find_mount_group_locked(const uuid_t *id)
106-
{
107-
struct mount_group *mg;
108-
109-
list_for_each_entry(mg, &mount_group_list, list) {
110-
if (uuid_equal(&mg->id, id))
111-
return mg;
112-
}
113-
return ERR_PTR(-ENOENT);
114-
}
115-
116-
static struct mount_group *__get_mount_group_locked(const uuid_t *id)
117-
{
118-
struct mount_group *mg;
119-
120-
mg = find_mount_group_locked(id);
121-
if (!IS_ERR(mg))
122-
return mg;
123-
124-
mg = kmalloc(sizeof(*mg), GFP_KERNEL);
125-
if (!mg)
126-
return ERR_PTR(-ENOMEM);
127-
kref_init(&mg->refcount);
128-
uuid_copy(&mg->id, id);
129-
mg->num_sessions = 0;
130-
spin_lock_init(&mg->lock);
131-
list_add(&mg->list, &mount_group_list);
132-
return mg;
133-
}
134-
135-
static struct mount_group *get_mount_group(const uuid_t *id)
136-
{
137-
struct mount_group *mg;
138-
139-
mutex_lock(&mount_group_list_lock);
140-
mg = __get_mount_group_locked(id);
141-
if (!IS_ERR(mg))
142-
kref_get(&mg->refcount);
143-
mutex_unlock(&mount_group_list_lock);
144-
145-
return mg;
146-
}
147-
148-
static void free_mount_group_list(void)
149-
{
150-
struct mount_group *mg, *tmp_mg;
151-
152-
list_for_each_entry_safe(mg, tmp_mg, &mount_group_list, list) {
153-
list_del_init(&mg->list);
154-
__mount_group_release(mg);
155-
}
156-
}
157-
15872
/**
15973
* dfs_cache_canonical_path - get a canonical DFS path
16074
*
@@ -704,7 +618,6 @@ void dfs_cache_destroy(void)
704618
{
705619
cancel_delayed_work_sync(&refresh_task);
706620
unload_nls(cache_cp);
707-
free_mount_group_list();
708621
flush_cache_ents();
709622
kmem_cache_destroy(cache_slab);
710623
destroy_workqueue(dfscache_wq);
@@ -1111,54 +1024,6 @@ int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iter
11111024
return rc;
11121025
}
11131026

1114-
/**
1115-
* dfs_cache_add_refsrv_session - add SMB session of referral server
1116-
*
1117-
* @mount_id: mount group uuid to lookup.
1118-
* @ses: reference counted SMB session of referral server.
1119-
*/
1120-
void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses)
1121-
{
1122-
struct mount_group *mg;
1123-
1124-
if (WARN_ON_ONCE(!mount_id || uuid_is_null(mount_id) || !ses))
1125-
return;
1126-
1127-
mg = get_mount_group(mount_id);
1128-
if (WARN_ON_ONCE(IS_ERR(mg)))
1129-
return;
1130-
1131-
spin_lock(&mg->lock);
1132-
if (mg->num_sessions < ARRAY_SIZE(mg->sessions))
1133-
mg->sessions[mg->num_sessions++] = ses;
1134-
spin_unlock(&mg->lock);
1135-
kref_put(&mg->refcount, mount_group_release);
1136-
}
1137-
1138-
/**
1139-
* dfs_cache_put_refsrv_sessions - put all referral server sessions
1140-
*
1141-
* Put all SMB sessions from the given mount group id.
1142-
*
1143-
* @mount_id: mount group uuid to lookup.
1144-
*/
1145-
void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id)
1146-
{
1147-
struct mount_group *mg;
1148-
1149-
if (!mount_id || uuid_is_null(mount_id))
1150-
return;
1151-
1152-
mutex_lock(&mount_group_list_lock);
1153-
mg = find_mount_group_locked(mount_id);
1154-
if (IS_ERR(mg)) {
1155-
mutex_unlock(&mount_group_list_lock);
1156-
return;
1157-
}
1158-
mutex_unlock(&mount_group_list_lock);
1159-
kref_put(&mg->refcount, mount_group_release);
1160-
}
1161-
11621027
/* Extract share from DFS target and return a pointer to prefix path or NULL */
11631028
static const char *parse_target_share(const char *target, char **share)
11641029
{
@@ -1384,11 +1249,6 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
13841249
cifs_dbg(FYI, "%s: not a dfs mount\n", __func__);
13851250
return 0;
13861251
}
1387-
1388-
if (uuid_is_null(&cifs_sb->dfs_mount_id)) {
1389-
cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__);
1390-
return -EINVAL;
1391-
}
13921252
/*
13931253
* After reconnecting to a different server, unique ids won't match anymore, so we disable
13941254
* serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).

fs/cifs/dfs_cache.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iter
4040
struct dfs_info3_param *ref);
4141
int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share,
4242
char **prefix);
43-
void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id);
44-
void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses);
4543
char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap);
4644
int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb);
4745

0 commit comments

Comments
 (0)