Skip to content

Commit c746f8a

Browse files
committed
cifs: fix potential deadlock in direct reclaim
jira VULN-169227 cve-pre CVE-2023-53751 commit-author Vincent Whitchurch <vincent.whitchurch@axis.com> commit cc391b6 upstream-diff | Multiple differences across many files due to large discrepancies between upstream and LTS 8.6, HOWEVER, the commit was obtained in the exact same way as on upstream, namely: 1. locating all usages of `TCP_Server_Info::srv_mutex', 2. changing all `mutex_lock(&<server_ptr>->srv_mutex)' to `cifs_server_lock(<server_ptr>)', 3. changing all `mutex_unlock(&<server_ptr>->srv_mutex)' to `cifs_server_unlock(<server_ptr>)', 4. renaming `srv_mutex~' to `_srv_mutex' to make sure nothing was missed. In this regard the commit doesn't differ from the upstream. The srv_mutex is used during writeback so cifs should ensure that allocations done when that mutex is held are done with GFP_NOFS, to avoid having direct reclaim ending up waiting for the same mutex and causing a deadlock. This is detected by lockdep with the splat below: ====================================================== WARNING: possible circular locking dependency detected 5.18.0 #70 Not tainted ------------------------------------------------------ kswapd0/49 is trying to acquire lock: ffff8880195782e0 (&tcp_ses->srv_mutex){+.+.}-{3:3}, at: compound_send_recv but task is already holding lock: ffffffffa98e66c0 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (fs_reclaim){+.+.}-{0:0}: fs_reclaim_acquire kmem_cache_alloc_trace __request_module crypto_alg_mod_lookup crypto_alloc_tfm_node crypto_alloc_shash cifs_alloc_hash smb311_crypto_shash_allocate smb311_update_preauth_hash compound_send_recv cifs_send_recv SMB2_negotiate smb2_negotiate cifs_negotiate_protocol cifs_get_smb_ses cifs_mount cifs_smb3_do_mount smb3_get_tree vfs_get_tree path_mount __x64_sys_mount do_syscall_64 entry_SYSCALL_64_after_hwframe -> #0 (&tcp_ses->srv_mutex){+.+.}-{3:3}: __lock_acquire lock_acquire __mutex_lock mutex_lock_nested compound_send_recv cifs_send_recv SMB2_write smb2_sync_write cifs_write cifs_writepage_locked cifs_writepage shrink_page_list shrink_lruvec shrink_node balance_pgdat kswapd kthread ret_from_fork other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(fs_reclaim); lock(&tcp_ses->srv_mutex); lock(fs_reclaim); lock(&tcp_ses->srv_mutex); *** DEADLOCK *** 1 lock held by kswapd0/49: #0: ffffffffa98e66c0 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat stack backtrace: CPU: 2 PID: 49 Comm: kswapd0 Not tainted 5.18.0 #70 Call Trace: <TASK> dump_stack_lvl dump_stack print_circular_bug.cold check_noncircular __lock_acquire lock_acquire __mutex_lock mutex_lock_nested compound_send_recv cifs_send_recv SMB2_write smb2_sync_write cifs_write cifs_writepage_locked cifs_writepage shrink_page_list shrink_lruvec shrink_node balance_pgdat kswapd kthread ret_from_fork </TASK> Fix this by using the memalloc_nofs_save/restore APIs around the places where the srv_mutex is held. Do this in a wrapper function for the lock/unlock of the srv_mutex, and rename the srv_mutex to avoid missing call sites in the conversion. Note that there is another lockdep warning involving internal crypto locks, which was masked by this problem and is visible after this fix, see the discussion in this thread: https://lore.kernel.org/all/20220523123755.GA13668@axis.com/ Link: https://lore.kernel.org/r/CANT5p=rqcYfYMVHirqvdnnca4Mo+JQSw5Qu12v=kPfpk5yhhmg@mail.gmail.com/ Reported-by: Shyam Prasad N <nspmangalore@gmail.com> Suggested-by: Lars Persson <larper@axis.com> Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com> Reviewed-by: Enzo Matsumiya <ematsumiya@suse.de> Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com> Signed-off-by: Steve French <stfrench@microsoft.com> (cherry picked from commit cc391b6) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
1 parent 81026fb commit c746f8a

9 files changed

Lines changed: 60 additions & 42 deletions

File tree

fs/cifs/cifs_swn.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ static int cifs_swn_store_swn_addr(const struct sockaddr_storage *new,
482482
static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *addr)
483483
{
484484
/* Store the reconnect address */
485-
mutex_lock(&tcon->ses->server->srv_mutex);
485+
cifs_server_lock(tcon->ses->server);
486486
if (!cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr)) {
487487
int ret;
488488

@@ -520,7 +520,7 @@ static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *a
520520
tcon->ses->server->tcpStatus = CifsNeedReconnect;
521521
spin_unlock(&GlobalMid_Lock);
522522
}
523-
mutex_unlock(&tcon->ses->server->srv_mutex);
523+
cifs_server_unlock(tcon->ses->server);
524524

525525
return 0;
526526
}

fs/cifs/cifsencrypt.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,9 +245,9 @@ int cifs_verify_signature(struct smb_rqst *rqst,
245245
cpu_to_le32(expected_sequence_number);
246246
cifs_pdu->Signature.Sequence.Reserved = 0;
247247

248-
mutex_lock(&server->srv_mutex);
248+
cifs_server_lock(server);
249249
rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be);
250-
mutex_unlock(&server->srv_mutex);
250+
cifs_server_unlock(server);
251251

252252
if (rc)
253253
return rc;
@@ -716,7 +716,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
716716

717717
memcpy(ses->auth_key.response + baselen, tiblob, tilen);
718718

719-
mutex_lock(&ses->server->srv_mutex);
719+
cifs_server_lock(ses->server);
720720

721721
rc = cifs_alloc_hash("hmac(md5)",
722722
&ses->server->secmech.hmacmd5,
@@ -768,7 +768,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
768768
cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
769769

770770
unlock:
771-
mutex_unlock(&ses->server->srv_mutex);
771+
cifs_server_unlock(ses->server);
772772
setup_ntlmv2_rsp_ret:
773773
kfree(tiblob);
774774

fs/cifs/cifsglob.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <linux/mm.h>
2727
#include <linux/mempool.h>
2828
#include <linux/workqueue.h>
29+
#include <linux/sched/mm.h>
2930
#include "cifs_fs_sb.h"
3031
#include "cifsacl.h"
3132
#include <crypto/internal/hash.h>
@@ -603,7 +604,8 @@ struct TCP_Server_Info {
603604
unsigned int in_flight; /* number of requests on the wire to server */
604605
unsigned int max_in_flight; /* max number of requests that were on wire */
605606
spinlock_t req_lock; /* protect the two values above */
606-
struct mutex srv_mutex;
607+
struct mutex _srv_mutex;
608+
unsigned int nofs_flag;
607609
struct task_struct *tsk;
608610
char server_GUID[16];
609611
__u16 sec_mode;
@@ -695,6 +697,22 @@ struct TCP_Server_Info {
695697
#endif
696698
};
697699

700+
static inline void cifs_server_lock(struct TCP_Server_Info *server)
701+
{
702+
unsigned int nofs_flag = memalloc_nofs_save();
703+
704+
mutex_lock(&server->_srv_mutex);
705+
server->nofs_flag = nofs_flag;
706+
}
707+
708+
static inline void cifs_server_unlock(struct TCP_Server_Info *server)
709+
{
710+
unsigned int nofs_flag = server->nofs_flag;
711+
712+
mutex_unlock(&server->_srv_mutex);
713+
memalloc_nofs_restore(nofs_flag);
714+
}
715+
698716
struct cifs_credits {
699717
unsigned int value;
700718
unsigned int instance;

fs/cifs/connect.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
261261

262262
/* do not want to be sending data on a socket we are freeing */
263263
cifs_dbg(FYI, "%s: tearing down socket\n", __func__);
264-
mutex_lock(&server->srv_mutex);
264+
cifs_server_lock(server);
265265
if (server->ssocket) {
266266
cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n",
267267
server->ssocket->state, server->ssocket->flags);
@@ -291,7 +291,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
291291
mid_entry->mid_flags |= MID_DELETED;
292292
}
293293
spin_unlock(&GlobalMid_Lock);
294-
mutex_unlock(&server->srv_mutex);
294+
cifs_server_unlock(server);
295295

296296
cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
297297
list_for_each_safe(tmp, tmp2, &retry_list) {
@@ -302,15 +302,15 @@ cifs_reconnect(struct TCP_Server_Info *server)
302302
}
303303

304304
if (cifs_rdma_enabled(server)) {
305-
mutex_lock(&server->srv_mutex);
305+
cifs_server_lock(server);
306306
smbd_destroy(server);
307-
mutex_unlock(&server->srv_mutex);
307+
cifs_server_unlock(server);
308308
}
309309

310310
do {
311311
try_to_freeze();
312312

313-
mutex_lock(&server->srv_mutex);
313+
cifs_server_lock(server);
314314

315315
#ifdef CONFIG_CIFS_SWN_UPCALL
316316
if (server->use_swn_dstaddr) {
@@ -352,7 +352,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
352352
rc = generic_ip_connect(server);
353353
if (rc) {
354354
cifs_dbg(FYI, "reconnect error %d\n", rc);
355-
mutex_unlock(&server->srv_mutex);
355+
cifs_server_unlock(server);
356356
msleep(3000);
357357
} else {
358358
atomic_inc(&tcpSesReconnectCount);
@@ -364,7 +364,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
364364
#ifdef CONFIG_CIFS_SWN_UPCALL
365365
server->use_swn_dstaddr = false;
366366
#endif
367-
mutex_unlock(&server->srv_mutex);
367+
cifs_server_unlock(server);
368368
}
369369
} while (server->tcpStatus == CifsNeedReconnect);
370370

@@ -1332,7 +1332,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
13321332
init_waitqueue_head(&tcp_ses->response_q);
13331333
init_waitqueue_head(&tcp_ses->request_q);
13341334
INIT_LIST_HEAD(&tcp_ses->pending_mid_q);
1335-
mutex_init(&tcp_ses->srv_mutex);
1335+
mutex_init(&tcp_ses->_srv_mutex);
13361336
memcpy(tcp_ses->workstation_RFC1001_name,
13371337
ctx->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
13381338
memcpy(tcp_ses->server_RFC1001_name,

fs/cifs/sess.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -903,14 +903,14 @@ sess_establish_session(struct sess_data *sess_data)
903903
{
904904
struct cifs_ses *ses = sess_data->ses;
905905

906-
mutex_lock(&ses->server->srv_mutex);
906+
cifs_server_lock(ses->server);
907907
if (!ses->server->session_estab) {
908908
if (ses->server->sign) {
909909
ses->server->session_key.response =
910910
kmemdup(ses->auth_key.response,
911911
ses->auth_key.len, GFP_KERNEL);
912912
if (!ses->server->session_key.response) {
913-
mutex_unlock(&ses->server->srv_mutex);
913+
cifs_server_unlock(ses->server);
914914
return -ENOMEM;
915915
}
916916
ses->server->session_key.len =
@@ -919,7 +919,7 @@ sess_establish_session(struct sess_data *sess_data)
919919
ses->server->sequence_number = 0x2;
920920
ses->server->session_estab = true;
921921
}
922-
mutex_unlock(&ses->server->srv_mutex);
922+
cifs_server_unlock(ses->server);
923923

924924
cifs_dbg(FYI, "CIFS session established successfully\n");
925925
spin_lock(&GlobalMid_Lock);

fs/cifs/smb1ops.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
4949
in_buf->WordCount = 0;
5050
put_bcc(0, in_buf);
5151

52-
mutex_lock(&server->srv_mutex);
52+
cifs_server_lock(server);
5353
rc = cifs_sign_smb(in_buf, server, &mid->sequence_number);
5454
if (rc) {
55-
mutex_unlock(&server->srv_mutex);
55+
cifs_server_unlock(server);
5656
return rc;
5757
}
5858

@@ -66,7 +66,7 @@ send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
6666
if (rc < 0)
6767
server->sequence_number--;
6868

69-
mutex_unlock(&server->srv_mutex);
69+
cifs_server_unlock(server);
7070

7171
cifs_dbg(FYI, "issued NT_CANCEL for mid %u, rc = %d\n",
7272
get_mid(in_buf), rc);

fs/cifs/smb2pdu.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,21 +1264,21 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
12641264
struct cifs_ses *ses = sess_data->ses;
12651265
struct TCP_Server_Info *server = cifs_ses_server(ses);
12661266

1267-
mutex_lock(&server->srv_mutex);
1267+
cifs_server_lock(server);
12681268
if (server->ops->generate_signingkey) {
12691269
rc = server->ops->generate_signingkey(ses);
12701270
if (rc) {
12711271
cifs_dbg(FYI,
12721272
"SMB3 session key generation failed\n");
1273-
mutex_unlock(&server->srv_mutex);
1273+
cifs_server_unlock(server);
12741274
return rc;
12751275
}
12761276
}
12771277
if (!server->session_estab) {
12781278
server->sequence_number = 0x2;
12791279
server->session_estab = true;
12801280
}
1281-
mutex_unlock(&server->srv_mutex);
1281+
cifs_server_unlock(server);
12821282

12831283
cifs_dbg(FYI, "SMB2/3 session established successfully\n");
12841284
/* keep existing ses state if binding */

fs/cifs/smbdirect.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,9 +1381,9 @@ void smbd_destroy(struct TCP_Server_Info *server)
13811381
log_rdma_event(INFO, "freeing mr list\n");
13821382
wake_up_interruptible_all(&info->wait_mr);
13831383
while (atomic_read(&info->mr_used_count)) {
1384-
mutex_unlock(&server->srv_mutex);
1384+
cifs_server_unlock(server);
13851385
msleep(1000);
1386-
mutex_lock(&server->srv_mutex);
1386+
cifs_server_lock(server);
13871387
}
13881388
destroy_mr_list(info);
13891389

fs/cifs/transport.c

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -790,22 +790,22 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
790790
} else
791791
instance = exist_credits->instance;
792792

793-
mutex_lock(&server->srv_mutex);
793+
cifs_server_lock(server);
794794

795795
/*
796796
* We can't use credits obtained from the previous session to send this
797797
* request. Check if there were reconnects after we obtained credits and
798798
* return -EAGAIN in such cases to let callers handle it.
799799
*/
800800
if (instance != server->reconnect_instance) {
801-
mutex_unlock(&server->srv_mutex);
801+
cifs_server_unlock(server);
802802
add_credits_and_wake_if(server, &credits, optype);
803803
return -EAGAIN;
804804
}
805805

806806
mid = server->ops->setup_async_request(server, rqst);
807807
if (IS_ERR(mid)) {
808-
mutex_unlock(&server->srv_mutex);
808+
cifs_server_unlock(server);
809809
add_credits_and_wake_if(server, &credits, optype);
810810
return PTR_ERR(mid);
811811
}
@@ -836,7 +836,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
836836
cifs_delete_mid(mid);
837837
}
838838

839-
mutex_unlock(&server->srv_mutex);
839+
cifs_server_unlock(server);
840840

841841
if (rc == 0)
842842
return 0;
@@ -1078,7 +1078,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
10781078
* of smb data.
10791079
*/
10801080

1081-
mutex_lock(&server->srv_mutex);
1081+
cifs_server_lock(server);
10821082

10831083
/*
10841084
* All the parts of the compound chain belong obtained credits from the
@@ -1088,7 +1088,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
10881088
* handle it.
10891089
*/
10901090
if (instance != server->reconnect_instance) {
1091-
mutex_unlock(&server->srv_mutex);
1091+
cifs_server_unlock(server);
10921092
for (j = 0; j < num_rqst; j++)
10931093
add_credits(server, &credits[j], optype);
10941094
return -EAGAIN;
@@ -1100,7 +1100,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
11001100
revert_current_mid(server, i);
11011101
for (j = 0; j < i; j++)
11021102
cifs_delete_mid(midQ[j]);
1103-
mutex_unlock(&server->srv_mutex);
1103+
cifs_server_unlock(server);
11041104

11051105
/* Update # of requests on wire to server */
11061106
for (j = 0; j < num_rqst; j++)
@@ -1132,7 +1132,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
11321132
server->sequence_number -= 2;
11331133
}
11341134

1135-
mutex_unlock(&server->srv_mutex);
1135+
cifs_server_unlock(server);
11361136

11371137
/*
11381138
* If sending failed for some reason or it is an oplock break that we
@@ -1337,19 +1337,19 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
13371337
and avoid races inside tcp sendmsg code that could cause corruption
13381338
of smb data */
13391339

1340-
mutex_lock(&server->srv_mutex);
1340+
cifs_server_lock(server);
13411341

13421342
rc = allocate_mid(ses, in_buf, &midQ);
13431343
if (rc) {
1344-
mutex_unlock(&server->srv_mutex);
1344+
cifs_server_unlock(server);
13451345
/* Update # of requests on wire to server */
13461346
add_credits(server, &credits, 0);
13471347
return rc;
13481348
}
13491349

13501350
rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
13511351
if (rc) {
1352-
mutex_unlock(&server->srv_mutex);
1352+
cifs_server_unlock(server);
13531353
goto out;
13541354
}
13551355

@@ -1363,7 +1363,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
13631363
if (rc < 0)
13641364
server->sequence_number -= 2;
13651365

1366-
mutex_unlock(&server->srv_mutex);
1366+
cifs_server_unlock(server);
13671367

13681368
if (rc < 0)
13691369
goto out;
@@ -1479,18 +1479,18 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
14791479
and avoid races inside tcp sendmsg code that could cause corruption
14801480
of smb data */
14811481

1482-
mutex_lock(&server->srv_mutex);
1482+
cifs_server_lock(server);
14831483

14841484
rc = allocate_mid(ses, in_buf, &midQ);
14851485
if (rc) {
1486-
mutex_unlock(&server->srv_mutex);
1486+
cifs_server_unlock(server);
14871487
return rc;
14881488
}
14891489

14901490
rc = cifs_sign_smb(in_buf, server, &midQ->sequence_number);
14911491
if (rc) {
14921492
cifs_delete_mid(midQ);
1493-
mutex_unlock(&server->srv_mutex);
1493+
cifs_server_unlock(server);
14941494
return rc;
14951495
}
14961496

@@ -1503,7 +1503,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
15031503
if (rc < 0)
15041504
server->sequence_number -= 2;
15051505

1506-
mutex_unlock(&server->srv_mutex);
1506+
cifs_server_unlock(server);
15071507

15081508
if (rc < 0) {
15091509
cifs_delete_mid(midQ);

0 commit comments

Comments
 (0)