Commit a7cfe776 authored by Paolo Abeni's avatar Paolo Abeni Committed by David S. Miller

mptcp: fix data races on local_id

The local address id is accessed lockless by the NL PM, add
all the required ONCE annotation. There is a caveat: the local
id can be initialized late in the subflow life-cycle, and its
validity is controlled by the local_id_valid flag.

Remove such flag and encode the validity in the local_id field
itself with negative value before initialization. That allows
accessing the field consistently with a single read operation.

Fixes: 0ee4261a ("mptcp: implement mptcp_pm_remove_subflow")
Cc: stable@vger.kernel.org
Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
Reviewed-by: default avatarMat Martineau <martineau@kernel.org>
Signed-off-by: default avatarMatthieu Baerts (NGI0) <matttbe@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b8adb69a
...@@ -65,7 +65,7 @@ static int subflow_get_info(struct sock *sk, struct sk_buff *skb) ...@@ -65,7 +65,7 @@ static int subflow_get_info(struct sock *sk, struct sk_buff *skb)
sf->map_data_len) || sf->map_data_len) ||
nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_FLAGS, flags) || nla_put_u32(skb, MPTCP_SUBFLOW_ATTR_FLAGS, flags) ||
nla_put_u8(skb, MPTCP_SUBFLOW_ATTR_ID_REM, sf->remote_id) || nla_put_u8(skb, MPTCP_SUBFLOW_ATTR_ID_REM, sf->remote_id) ||
nla_put_u8(skb, MPTCP_SUBFLOW_ATTR_ID_LOC, sf->local_id)) { nla_put_u8(skb, MPTCP_SUBFLOW_ATTR_ID_LOC, subflow_get_local_id(sf))) {
err = -EMSGSIZE; err = -EMSGSIZE;
goto nla_failure; goto nla_failure;
} }
......
...@@ -800,7 +800,7 @@ static void mptcp_pm_nl_rm_addr_or_subflow(struct mptcp_sock *msk, ...@@ -800,7 +800,7 @@ static void mptcp_pm_nl_rm_addr_or_subflow(struct mptcp_sock *msk,
mptcp_for_each_subflow_safe(msk, subflow, tmp) { mptcp_for_each_subflow_safe(msk, subflow, tmp) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow); struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
int how = RCV_SHUTDOWN | SEND_SHUTDOWN; int how = RCV_SHUTDOWN | SEND_SHUTDOWN;
u8 id = subflow->local_id; u8 id = subflow_get_local_id(subflow);
if (rm_type == MPTCP_MIB_RMADDR && subflow->remote_id != rm_id) if (rm_type == MPTCP_MIB_RMADDR && subflow->remote_id != rm_id)
continue; continue;
...@@ -809,7 +809,7 @@ static void mptcp_pm_nl_rm_addr_or_subflow(struct mptcp_sock *msk, ...@@ -809,7 +809,7 @@ static void mptcp_pm_nl_rm_addr_or_subflow(struct mptcp_sock *msk,
pr_debug(" -> %s rm_list_ids[%d]=%u local_id=%u remote_id=%u mpc_id=%u", pr_debug(" -> %s rm_list_ids[%d]=%u local_id=%u remote_id=%u mpc_id=%u",
rm_type == MPTCP_MIB_RMADDR ? "address" : "subflow", rm_type == MPTCP_MIB_RMADDR ? "address" : "subflow",
i, rm_id, subflow->local_id, subflow->remote_id, i, rm_id, id, subflow->remote_id,
msk->mpc_endpoint_id); msk->mpc_endpoint_id);
spin_unlock_bh(&msk->pm.lock); spin_unlock_bh(&msk->pm.lock);
mptcp_subflow_shutdown(sk, ssk, how); mptcp_subflow_shutdown(sk, ssk, how);
...@@ -1994,7 +1994,7 @@ static int mptcp_event_add_subflow(struct sk_buff *skb, const struct sock *ssk) ...@@ -1994,7 +1994,7 @@ static int mptcp_event_add_subflow(struct sk_buff *skb, const struct sock *ssk)
if (WARN_ON_ONCE(!sf)) if (WARN_ON_ONCE(!sf))
return -EINVAL; return -EINVAL;
if (nla_put_u8(skb, MPTCP_ATTR_LOC_ID, sf->local_id)) if (nla_put_u8(skb, MPTCP_ATTR_LOC_ID, subflow_get_local_id(sf)))
return -EMSGSIZE; return -EMSGSIZE;
if (nla_put_u8(skb, MPTCP_ATTR_REM_ID, sf->remote_id)) if (nla_put_u8(skb, MPTCP_ATTR_REM_ID, sf->remote_id))
......
...@@ -234,7 +234,7 @@ static int mptcp_userspace_pm_remove_id_zero_address(struct mptcp_sock *msk, ...@@ -234,7 +234,7 @@ static int mptcp_userspace_pm_remove_id_zero_address(struct mptcp_sock *msk,
lock_sock(sk); lock_sock(sk);
mptcp_for_each_subflow(msk, subflow) { mptcp_for_each_subflow(msk, subflow) {
if (subflow->local_id == 0) { if (READ_ONCE(subflow->local_id) == 0) {
has_id_0 = true; has_id_0 = true;
break; break;
} }
......
...@@ -85,7 +85,7 @@ static int __mptcp_socket_create(struct mptcp_sock *msk) ...@@ -85,7 +85,7 @@ static int __mptcp_socket_create(struct mptcp_sock *msk)
subflow->subflow_id = msk->subflow_id++; subflow->subflow_id = msk->subflow_id++;
/* This is the first subflow, always with id 0 */ /* This is the first subflow, always with id 0 */
subflow->local_id_valid = 1; WRITE_ONCE(subflow->local_id, 0);
mptcp_sock_graft(msk->first, sk->sk_socket); mptcp_sock_graft(msk->first, sk->sk_socket);
iput(SOCK_INODE(ssock)); iput(SOCK_INODE(ssock));
......
...@@ -491,10 +491,9 @@ struct mptcp_subflow_context { ...@@ -491,10 +491,9 @@ struct mptcp_subflow_context {
remote_key_valid : 1, /* received the peer key from */ remote_key_valid : 1, /* received the peer key from */
disposable : 1, /* ctx can be free at ulp release time */ disposable : 1, /* ctx can be free at ulp release time */
stale : 1, /* unable to snd/rcv data, do not use for xmit */ stale : 1, /* unable to snd/rcv data, do not use for xmit */
local_id_valid : 1, /* local_id is correctly initialized */
valid_csum_seen : 1, /* at least one csum validated */ valid_csum_seen : 1, /* at least one csum validated */
is_mptfo : 1, /* subflow is doing TFO */ is_mptfo : 1, /* subflow is doing TFO */
__unused : 9; __unused : 10;
bool data_avail; bool data_avail;
bool scheduled; bool scheduled;
u32 remote_nonce; u32 remote_nonce;
...@@ -505,7 +504,7 @@ struct mptcp_subflow_context { ...@@ -505,7 +504,7 @@ struct mptcp_subflow_context {
u8 hmac[MPTCPOPT_HMAC_LEN]; /* MPJ subflow only */ u8 hmac[MPTCPOPT_HMAC_LEN]; /* MPJ subflow only */
u64 iasn; /* initial ack sequence number, MPC subflows only */ u64 iasn; /* initial ack sequence number, MPC subflows only */
}; };
u8 local_id; s16 local_id; /* if negative not initialized yet */
u8 remote_id; u8 remote_id;
u8 reset_seen:1; u8 reset_seen:1;
u8 reset_transient:1; u8 reset_transient:1;
...@@ -556,6 +555,7 @@ mptcp_subflow_ctx_reset(struct mptcp_subflow_context *subflow) ...@@ -556,6 +555,7 @@ mptcp_subflow_ctx_reset(struct mptcp_subflow_context *subflow)
{ {
memset(&subflow->reset, 0, sizeof(subflow->reset)); memset(&subflow->reset, 0, sizeof(subflow->reset));
subflow->request_mptcp = 1; subflow->request_mptcp = 1;
WRITE_ONCE(subflow->local_id, -1);
} }
static inline u64 static inline u64
...@@ -1022,6 +1022,15 @@ int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc); ...@@ -1022,6 +1022,15 @@ int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc);
int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skc); int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skc);
int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skc); int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skc);
static inline u8 subflow_get_local_id(const struct mptcp_subflow_context *subflow)
{
int local_id = READ_ONCE(subflow->local_id);
if (local_id < 0)
return 0;
return local_id;
}
void __init mptcp_pm_nl_init(void); void __init mptcp_pm_nl_init(void);
void mptcp_pm_nl_work(struct mptcp_sock *msk); void mptcp_pm_nl_work(struct mptcp_sock *msk);
void mptcp_pm_nl_rm_subflow_received(struct mptcp_sock *msk, void mptcp_pm_nl_rm_subflow_received(struct mptcp_sock *msk,
......
...@@ -577,8 +577,8 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb) ...@@ -577,8 +577,8 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
static void subflow_set_local_id(struct mptcp_subflow_context *subflow, int local_id) static void subflow_set_local_id(struct mptcp_subflow_context *subflow, int local_id)
{ {
subflow->local_id = local_id; WARN_ON_ONCE(local_id < 0 || local_id > 255);
subflow->local_id_valid = 1; WRITE_ONCE(subflow->local_id, local_id);
} }
static int subflow_chk_local_id(struct sock *sk) static int subflow_chk_local_id(struct sock *sk)
...@@ -587,7 +587,7 @@ static int subflow_chk_local_id(struct sock *sk) ...@@ -587,7 +587,7 @@ static int subflow_chk_local_id(struct sock *sk)
struct mptcp_sock *msk = mptcp_sk(subflow->conn); struct mptcp_sock *msk = mptcp_sk(subflow->conn);
int err; int err;
if (likely(subflow->local_id_valid)) if (likely(subflow->local_id >= 0))
return 0; return 0;
err = mptcp_pm_get_local_id(msk, (struct sock_common *)sk); err = mptcp_pm_get_local_id(msk, (struct sock_common *)sk);
...@@ -1731,6 +1731,7 @@ static struct mptcp_subflow_context *subflow_create_ctx(struct sock *sk, ...@@ -1731,6 +1731,7 @@ static struct mptcp_subflow_context *subflow_create_ctx(struct sock *sk,
pr_debug("subflow=%p", ctx); pr_debug("subflow=%p", ctx);
ctx->tcp_sock = sk; ctx->tcp_sock = sk;
WRITE_ONCE(ctx->local_id, -1);
return ctx; return ctx;
} }
...@@ -1966,7 +1967,7 @@ static void subflow_ulp_clone(const struct request_sock *req, ...@@ -1966,7 +1967,7 @@ static void subflow_ulp_clone(const struct request_sock *req,
new_ctx->idsn = subflow_req->idsn; new_ctx->idsn = subflow_req->idsn;
/* this is the first subflow, id is always 0 */ /* this is the first subflow, id is always 0 */
new_ctx->local_id_valid = 1; subflow_set_local_id(new_ctx, 0);
} else if (subflow_req->mp_join) { } else if (subflow_req->mp_join) {
new_ctx->ssn_offset = subflow_req->ssn_offset; new_ctx->ssn_offset = subflow_req->ssn_offset;
new_ctx->mp_join = 1; new_ctx->mp_join = 1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment