Skip to content

Commit 3d964a2

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()'. Apart from guarding the `server->hostname' value change some elements of the commit bbcce36 were included as well: - Used helper local variable `hostname' to store the value of `extract_hostname()' to avoid calling it while locked, which could lead to a schedule while atomic. - Avoided assigning to `server->hostname' the result of `extract_hostname()' call in case it ended with error. Otherwise `server->hostname' would contain rubbish. Added a debug message informing about that case. - Added initialization of `TCP_Server_Info::srv_lock' in `cifs_get_tcp_session()' as it's done in d7d7a66. - 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 9d7bba3 commit 3d964a2

4 files changed

Lines changed: 31 additions & 16 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: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ static void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
130130
struct dfs_cache_tgt_iterator **tgt_it)
131131
{
132132
const char *name;
133+
char *hostname;
133134
int rc;
134135

135136
if (!cifs_sb || !cifs_sb->origin_fullpath)
@@ -147,13 +148,18 @@ static void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
147148

148149
name = dfs_cache_get_tgt_name(*tgt_it);
149150

150-
kfree(server->hostname);
151-
152-
server->hostname = extract_hostname(name);
153-
if (IS_ERR(server->hostname)) {
151+
hostname = extract_hostname(name);
152+
if (!IS_ERR(hostname)) {
153+
spin_lock(&server->srv_lock);
154+
kfree(server->hostname);
155+
server->hostname = hostname;
156+
spin_unlock(&server->srv_lock);
157+
} else {
154158
cifs_dbg(FYI,
155159
"%s: failed to extract hostname from target: %ld\n",
156-
__func__, PTR_ERR(server->hostname));
160+
__func__, PTR_ERR(hostname));
161+
cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__,
162+
server->hostname);
157163
}
158164

159165
rc = reconn_set_ipaddr_from_hostname(server);
@@ -418,9 +424,7 @@ cifs_echo_request(struct work_struct *work)
418424
goto requeue_echo;
419425

420426
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);
427+
cifs_server_dbg(FYI, "send echo request: rc = %d\n", rc);
424428

425429
#ifdef CONFIG_CIFS_SWN_UPCALL
426430
/* Check witness registrations */
@@ -1194,8 +1198,12 @@ static int match_server(struct TCP_Server_Info *server, struct smb3_fs_context *
11941198
if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns))
11951199
return 0;
11961200

1197-
if (strcasecmp(server->hostname, ctx->server_hostname))
1201+
spin_lock(&server->srv_lock);
1202+
if (strcasecmp(server->hostname, ctx->server_hostname)) {
1203+
spin_unlock(&server->srv_lock);
11981204
return 0;
1205+
}
1206+
spin_unlock(&server->srv_lock);
11991207

12001208
if (!match_address(server, addr,
12011209
(struct sockaddr *)&ctx->srcaddr))
@@ -1343,6 +1351,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
13431351
tcp_ses->lstrp = jiffies;
13441352
tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression);
13451353
spin_lock_init(&tcp_ses->req_lock);
1354+
spin_lock_init(&tcp_ses->srv_lock);
13461355
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
13471356
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
13481357
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
@@ -1512,7 +1521,9 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
15121521
if (tcon == NULL)
15131522
return -ENOMEM;
15141523

1524+
spin_lock(&server->srv_lock);
15151525
scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);
1526+
spin_unlock(&server->srv_lock);
15161527

15171528
xid = get_xid();
15181529
tcon->ses = ses;

0 commit comments

Comments
 (0)