:p
atchew
Login
From: Geliang Tang <tanggeliang@kylinos.cn> In mptcp_pm_nl_remove_doit(), sk_omem_alloc is decremented immediately but the memory is freed later via kfree_rcu(). This allows a CAP_NET_ADMIN user to bypass the socket memory quota and exhaust kernel memory by accumulating RCU callbacks. Fix by using call_rcu() with a custom callback that uses sock_kfree_s() to free the entry and decrement sk_omem_alloc atomically. To ensure the socket remains valid until the callback runs, take a reference with sock_hold() when storing the socket pointer in the entry, and release it with sock_put() in the callback. Fixes: 13b4ece33cf9 ("mptcp: pm: Defer freeing of MPTCP userspace path manager entries") Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- This patch addresses the pre-existing issue Sashiko mentioned in https://sashiko.dev/#/patchset/cover.1782457962.git.tanggeliang@kylinos.cn. --- net/mptcp/pm_userspace.c | 18 +++++++++++++----- net/mptcp/protocol.h | 2 ++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index XXXXXXX..XXXXXXX 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -XXX,XX +XXX,XX @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk, ret = -ENOMEM; goto append_err; } + sock_hold(sk); + e->sk = sk; if (!e->addr.id && needs_id) e->addr.id = find_next_zero_bit(id_bitmap, @@ -XXX,XX +XXX,XX @@ void mptcp_pm_remove_addr_entry(struct mptcp_sock *msk, spin_unlock_bh(&msk->pm.lock); } +static void mptcp_userspace_pm_free_entry(struct rcu_head *head) +{ + struct mptcp_pm_addr_entry *entry = + container_of(head, struct mptcp_pm_addr_entry, rcu); + struct sock *sk = entry->sk; + + sock_kfree_s(sk, entry, sizeof(*entry)); + sock_put(sk); +} + int mptcp_pm_nl_remove_doit(struct sk_buff *skb, struct genl_info *info) { struct mptcp_pm_addr_entry *match; @@ -XXX,XX +XXX,XX @@ int mptcp_pm_nl_remove_doit(struct sk_buff *skb, struct genl_info *info) release_sock(sk); - kfree_rcu_mightsleep(match); - /* Adjust sk_omem_alloc like sock_kfree_s() does, to match - * with allocation of this memory by sock_kmemdup() - */ - atomic_sub(sizeof(*match), &sk->sk_omem_alloc); + call_rcu(&match->rcu, mptcp_userspace_pm_free_entry); err = 0; out: diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index XXXXXXX..XXXXXXX 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -XXX,XX +XXX,XX @@ struct mptcp_pm_addr_entry { u32 flags; int ifindex; struct socket *lsk; + struct sock *sk; + struct rcu_head rcu; }; struct mptcp_data_frag { -- 2.53.0
From: Geliang Tang <tanggeliang@kylinos.cn> In mptcp_pm_nl_remove_doit(), sk_omem_alloc is decremented immediately but the memory is freed later via kfree_rcu(). This allows a CAP_NET_ADMIN user to bypass the socket memory quota and exhaust kernel memory by accumulating RCU callbacks. Fix by using call_rcu() with a custom callback that uses sock_kfree_s() to free the entry and decrement sk_omem_alloc atomically. To ensure the socket remains valid until the callback runs, take a reference with sock_hold() when storing the socket pointer in the entry, and release it with sock_put() in the callback. Convert the synchronous freeing paths in free_local_addr_list() and delete_local_addr() to use the same RCU callback, ensuring the socket reference is properly released. Fixes: 13b4ece33cf9 ("mptcp: pm: Defer freeing of MPTCP userspace path manager entries") Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn> --- v2: - call mptcp_userspace_pm_free_entry in free_local_addr_list and delete_local_addr. v1: - Link: https://patchwork.kernel.org/project/mptcp/patch/9b443bafa57f40a51eb6a43f088ff37d71b39973.1782528088.git.tanggeliang@kylinos.cn/ This patch addresses the pre-existing issue Sashiko mentioned in https://sashiko.dev/#/patchset/cover.1782457962.git.tanggeliang@kylinos.cn. --- net/mptcp/pm_userspace.c | 24 +++++++++++++++--------- net/mptcp/protocol.h | 2 ++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index XXXXXXX..XXXXXXX 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -XXX,XX +XXX,XX @@ list_for_each_entry(__entry, \ &((__msk)->pm.userspace_pm_local_addr_list), list) +static void mptcp_userspace_pm_free_entry(struct rcu_head *head) +{ + struct mptcp_pm_addr_entry *entry = + container_of(head, struct mptcp_pm_addr_entry, rcu); + struct sock *sk = entry->sk; + + sock_kfree_s(sk, entry, sizeof(*entry)); + sock_put(sk); +} + void mptcp_userspace_pm_free_local_addr_list(struct mptcp_sock *msk) { struct mptcp_pm_addr_entry *entry, *tmp; - struct sock *sk = (struct sock *)msk; LIST_HEAD(free_list); spin_lock_bh(&msk->pm.lock); @@ -XXX,XX +XXX,XX @@ void mptcp_userspace_pm_free_local_addr_list(struct mptcp_sock *msk) spin_unlock_bh(&msk->pm.lock); list_for_each_entry_safe(entry, tmp, &free_list, list) { - sock_kfree_s(sk, entry, sizeof(*entry)); + call_rcu(&entry->rcu, mptcp_userspace_pm_free_entry); } } @@ -XXX,XX +XXX,XX @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk, ret = -ENOMEM; goto append_err; } + sock_hold(sk); + e->sk = sk; if (!e->addr.id && needs_id) e->addr.id = find_next_zero_bit(id_bitmap, @@ -XXX,XX +XXX,XX @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk, static int mptcp_userspace_pm_delete_local_addr(struct mptcp_sock *msk, struct mptcp_pm_addr_entry *addr) { - struct sock *sk = (struct sock *)msk; struct mptcp_pm_addr_entry *entry; entry = mptcp_userspace_pm_lookup_addr(msk, &addr->addr); @@ -XXX,XX +XXX,XX @@ static int mptcp_userspace_pm_delete_local_addr(struct mptcp_sock *msk, * be used multiple times (e.g. fullmesh mode). */ list_del_rcu(&entry->list); - sock_kfree_s(sk, entry, sizeof(*entry)); + call_rcu(&entry->rcu, mptcp_userspace_pm_free_entry); msk->pm.local_addr_used--; return 0; } @@ -XXX,XX +XXX,XX @@ int mptcp_pm_nl_remove_doit(struct sk_buff *skb, struct genl_info *info) release_sock(sk); - kfree_rcu_mightsleep(match); - /* Adjust sk_omem_alloc like sock_kfree_s() does, to match - * with allocation of this memory by sock_kmemdup() - */ - atomic_sub(sizeof(*match), &sk->sk_omem_alloc); + call_rcu(&match->rcu, mptcp_userspace_pm_free_entry); err = 0; out: diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index XXXXXXX..XXXXXXX 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -XXX,XX +XXX,XX @@ struct mptcp_pm_addr_entry { u32 flags; int ifindex; struct socket *lsk; + struct sock *sk; + struct rcu_head rcu; }; struct mptcp_data_frag { -- 2.53.0