Skip to content

Commit 377e3a8

Browse files
committed
cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname
jira VULN-169227 cve CVE-2023-53751 commit-author Paulo Alcantara <pc@manguebit.com> commit 90c49fc upstream-diff | fs/cifs/cifsglob.h Added `srv_lock' to the `TCP_Server_Info' struct as it was done in the non-backported commit d7d7a66 ("cifs: avoid use of global locks for high contention data"). fs/cifs/cifs_debug.c Ignored changes in `cifs_debug_data_proc_show()' because the code using `hostname' was only added in the non-backported commit 40f077a ("cifs: clarify hostname vs ip address in /proc/fs/cifs/DebugData"). fs/cifs/connect.c - Changed the `reconn_set_next_dfs_target()' function instead of `__reconnect_target_unlocked()' which doesn't exist in LTS 8.6. The `__reconnect_target_unlocked()' is where the `hostname' is "updated once or many times during reconnect" (see original commit message). The "reconnect" refers to the function `cifs_reconnect()', which calls `__reconnect_target_unlocked()' on a third level down the call tree. Relative to LTS 8.6 the `cifs_reconnect()' underwent major rewrites in the commits bbcce36 ("cifs: split out dfs code from cifs_reconnect()") and c88f7dc ("cifs: support nested dfs links over reconnect"). In LTS 8.6 the updating of `hostname' can be tracked down to the `reconn_set_next_dfs_target()' function called by `cifs_reconnect()'. - Added initialization of `TCP_Server_Info::srv_lock' in `cifs_get_tcp_session()' as it's done in d7d7a66. - In `match_server()' changed the lockdep assertion from holding `server->srv_lock' to holding `cifs_tcp_ses_lock', because this is the actual invariant for the LTS 8.6 version. - In `match_server()' sandwiched the `strcasecmp(server->hostname, ...)' call in the lock/unlock pair of `server->srv_lock', because in LTS 8.6 the `match_server()' is not called with the `server->srv_lock' held and yet this is the lock chosen to protect the `server->hostname' field. fs/cifs/sess.c Ignored changes in `cifs_try_adding_channels()' because the code using `hostname' was only added in the non-backported commit 9c2dc11 ("smb3: do not attempt multichannel to server which does not support it"). As a result no changes to the file have been made. TCP_Server_Info::hostname may be updated once or many times during reconnect, so protect its access outside reconnect path as well and then prevent any potential use-after-free bugs. Cc: stable@vger.kernel.org Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com> Signed-off-by: Steve French <stfrench@microsoft.com> (cherry picked from commit 90c49fc) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
1 parent 9d04f9d commit 377e3a8

4 files changed

Lines changed: 24 additions & 11 deletions

File tree

fs/cifs/cifs_debug.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -609,10 +609,13 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
609609
server->fastest_cmd[j],
610610
server->slowest_cmd[j]);
611611
for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++)
612-
if (atomic_read(&server->smb2slowcmd[j]))
612+
if (atomic_read(&server->smb2slowcmd[j])) {
613+
spin_lock(&server->srv_lock);
613614
seq_printf(m, " %d slow responses from %s for command %d\n",
614615
atomic_read(&server->smb2slowcmd[j]),
615616
server->hostname, j);
617+
spin_unlock(&server->srv_lock);
618+
}
616619
#endif /* STATS2 */
617620
list_for_each(tmp2, &server->smb_ses_list) {
618621
ses = list_entry(tmp2, struct cifs_ses,

fs/cifs/cifs_debug.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,19 +95,19 @@ do { \
9595

9696
#define cifs_server_dbg_func(ratefunc, type, fmt, ...) \
9797
do { \
98-
const char *sn = ""; \
99-
if (server && server->hostname) \
100-
sn = server->hostname; \
98+
spin_lock(&server->srv_lock); \
10199
if ((type) & FYI && cifsFYI & CIFS_INFO) { \
102100
pr_debug_ ## ratefunc("%s: \\\\%s " fmt, \
103-
__FILE__, sn, ##__VA_ARGS__); \
101+
__FILE__, server->hostname, \
102+
##__VA_ARGS__); \
104103
} else if ((type) & VFS) { \
105104
pr_err_ ## ratefunc("VFS: \\\\%s " fmt, \
106-
sn, ##__VA_ARGS__); \
105+
server->hostname, ##__VA_ARGS__); \
107106
} else if ((type) & NOISY && (NOISY != 0)) { \
108107
pr_debug_ ## ratefunc("\\\\%s " fmt, \
109-
sn, ##__VA_ARGS__); \
108+
server->hostname, ##__VA_ARGS__); \
110109
} \
110+
spin_unlock(&server->srv_lock); \
111111
} while (0)
112112

113113
#define cifs_server_dbg(type, fmt, ...) \

fs/cifs/cifsglob.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,7 @@ inc_rfc1001_len(void *buf, int count)
580580
struct TCP_Server_Info {
581581
struct list_head tcp_ses_list;
582582
struct list_head smb_ses_list;
583+
spinlock_t srv_lock; /* protect anything here that is not protected */
583584
int srv_count; /* reference counter */
584585
/* 15 character server name + 0x20 16th byte indicating type = srv */
585586
char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];

fs/cifs/connect.c

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,11 @@ static void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
147147

148148
name = dfs_cache_get_tgt_name(*tgt_it);
149149

150+
spin_lock(&server->srv_lock);
150151
kfree(server->hostname);
151152

152153
server->hostname = extract_hostname(name);
154+
spin_unlock(&server->srv_lock);
153155
if (IS_ERR(server->hostname)) {
154156
cifs_dbg(FYI,
155157
"%s: failed to extract hostname from target: %ld\n",
@@ -418,9 +420,7 @@ cifs_echo_request(struct work_struct *work)
418420
goto requeue_echo;
419421

420422
rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS;
421-
if (rc)
422-
cifs_dbg(FYI, "Unable to send echo request to server: %s\n",
423-
server->hostname);
423+
cifs_server_dbg(FYI, "send echo request: rc = %d\n", rc);
424424

425425
#ifdef CONFIG_CIFS_SWN_UPCALL
426426
/* Check witness registrations */
@@ -1177,6 +1177,8 @@ static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *
11771177
{
11781178
struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
11791179

1180+
lockdep_assert_held(&cifs_tcp_ses_lock);
1181+
11801182
if (ctx->nosharesock)
11811183
return 0;
11821184

@@ -1194,8 +1196,12 @@ static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *
11941196
if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns))
11951197
return 0;
11961198

1197-
if (strcasecmp(server->hostname, ctx->server_hostname))
1199+
spin_lock(&server->srv_lock);
1200+
if (strcasecmp(server->hostname, ctx->server_hostname)) {
1201+
spin_unlock(&server->srv_lock);
11981202
return 0;
1203+
}
1204+
spin_unlock(&server->srv_lock);
11991205

12001206
if (!match_address(server, addr,
12011207
(struct sockaddr *)&ctx->srcaddr))
@@ -1343,6 +1349,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
13431349
tcp_ses->lstrp = jiffies;
13441350
tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression);
13451351
spin_lock_init(&tcp_ses->req_lock);
1352+
spin_lock_init(&tcp_ses->srv_lock);
13461353
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
13471354
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
13481355
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
@@ -1512,7 +1519,9 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
15121519
if (tcon == NULL)
15131520
return -ENOMEM;
15141521

1522+
spin_lock(&server->srv_lock);
15151523
scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);
1524+
spin_unlock(&server->srv_lock);
15161525

15171526
xid = get_xid();
15181527
tcon->ses = ses;

0 commit comments

Comments
 (0)