Skip to content

Commit 1cec775

Browse files
kmjohansenintel-lab-lkp
authored andcommitted
mptcp: fix 'scheduling while atomic' in mptcp_pm_nl_append_new_local_addr
If multiple connection requests attempt to create an implicit mptcp endpoint in parallel, more than one caller may end up in mptcp_pm_nl_append_new_local_addr because none found the address in local_addr_list during their call to mptcp_pm_nl_get_local_id. In this case, the concurrent new_local_addr calls may delete the address entry created by the previous caller. These deletes use synchronize_rcu, but this is not permitted in some of the contexts where this function may be called. During packet recv, the caller may be in a rcu read critical section and have preemption disabled. An example stack: BUG: scheduling while atomic: swapper/2/0/0x00000302 Call Trace: <IRQ> dump_stack_lvl+0x76/0xa0 dump_stack+0x10/0x20 __schedule_bug+0x64/0x80 schedule_debug.constprop.0+0xdb/0x130 __schedule+0x69/0x6a0 schedule+0x33/0x110 schedule_timeout+0x157/0x170 wait_for_completion+0x88/0x150 __wait_rcu_gp+0x150/0x160 synchronize_rcu+0x12d/0x140 mptcp_pm_nl_append_new_local_addr+0x1bd/0x280 mptcp_pm_nl_get_local_id+0x121/0x160 mptcp_pm_get_local_id+0x9d/0xe0 subflow_check_req+0x1a8/0x460 subflow_v4_route_req+0xb5/0x110 tcp_conn_request+0x3a4/0xd00 subflow_v4_conn_request+0x42/0xa0 tcp_rcv_state_process+0x1e3/0x7e0 tcp_v4_do_rcv+0xd3/0x2a0 tcp_v4_rcv+0xbb8/0xbf0 ip_protocol_deliver_rcu+0x3c/0x210 ip_local_deliver_finish+0x77/0xa0 ip_local_deliver+0x6e/0x120 ip_sublist_rcv_finish+0x6f/0x80 ip_sublist_rcv+0x178/0x230 ip_list_rcv+0x102/0x140 __netif_receive_skb_list_core+0x22d/0x250 netif_receive_skb_list_internal+0x1a3/0x2d0 napi_complete_done+0x74/0x1c0 igb_poll+0x6c/0xe0 [igb] __napi_poll+0x30/0x200 net_rx_action+0x181/0x2e0 handle_softirqs+0xd8/0x340 __irq_exit_rcu+0xd9/0x100 irq_exit_rcu+0xe/0x20 common_interrupt+0xa4/0xb0 </IRQ> This problem seems particularly prevalent if the user advertises an endpoint that has a different external vs internal address. In the case where the external address is advertised and multiple connections already exist, multiple subflow SYNs arrive in parallel which tends to trigger the race during creation of the first local_addr_list entries which have the internal address instead. Fix by skipping the replacement of an existing implicit local address if called via mptcp_pm_nl_get_local_id. Cc: [email protected] Fixes: d045b9e ("mptcp: introduce implicit endpoints") Suggested-by: Paolo Abeni <[email protected]> Signed-off-by: Krister Johansen <[email protected]>
1 parent 384fa1d commit 1cec775

File tree

1 file changed

+15
-3
lines changed

1 file changed

+15
-3
lines changed

net/mptcp/pm_netlink.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -977,7 +977,7 @@ static void __mptcp_pm_release_addr_entry(struct mptcp_pm_addr_entry *entry)
977977

978978
static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet,
979979
struct mptcp_pm_addr_entry *entry,
980-
bool needs_id)
980+
bool needs_id, bool replace)
981981
{
982982
struct mptcp_pm_addr_entry *cur, *del_entry = NULL;
983983
unsigned int addr_max;
@@ -1017,6 +1017,17 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet,
10171017
if (entry->addr.id)
10181018
goto out;
10191019

1020+
/* allow callers that only need to look up the local
1021+
* addr's id to skip replacement. This allows them to
1022+
* avoid calling synchronize_rcu in the packet recv
1023+
* path.
1024+
*/
1025+
if (!replace) {
1026+
kfree(entry);
1027+
ret = cur->addr.id;
1028+
goto out;
1029+
}
1030+
10201031
pernet->addrs--;
10211032
entry->addr.id = cur->addr.id;
10221033
list_del_rcu(&cur->list);
@@ -1165,7 +1176,7 @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skc
11651176
entry->ifindex = 0;
11661177
entry->flags = MPTCP_PM_ADDR_FLAG_IMPLICIT;
11671178
entry->lsk = NULL;
1168-
ret = mptcp_pm_nl_append_new_local_addr(pernet, entry, true);
1179+
ret = mptcp_pm_nl_append_new_local_addr(pernet, entry, true, false);
11691180
if (ret < 0)
11701181
kfree(entry);
11711182

@@ -1433,7 +1444,8 @@ int mptcp_pm_nl_add_addr_doit(struct sk_buff *skb, struct genl_info *info)
14331444
}
14341445
}
14351446
ret = mptcp_pm_nl_append_new_local_addr(pernet, entry,
1436-
!mptcp_pm_has_addr_attr_id(attr, info));
1447+
!mptcp_pm_has_addr_attr_id(attr, info),
1448+
true);
14371449
if (ret < 0) {
14381450
GENL_SET_ERR_MSG_FMT(info, "too many addresses or duplicate one: %d", ret);
14391451
goto out_free;

0 commit comments

Comments
 (0)