Commit 400de0a2 authored by Matthieu Baerts (NGI0)'s avatar Matthieu Baerts (NGI0) Committed by Liu Jian
Browse files

mptcp: pm: avoid possible UaF when selecting endp

mainline inclusion
from mainline-v6.11-rc5
commit 48e50dcbcbaaf713d82bf2da5c16aeced94ad07d
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IAOY17
CVE: CVE-2024-44974

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=48e50dcbcbaaf713d82bf2da5c16aeced94ad07d



-------------------------------------------------

select_local_address() and select_signal_address() both select an
endpoint entry from the list inside an RCU protected section, but return
a reference to it, to be read later on. If the entry is dereferenced
after the RCU unlock, reading info could cause a Use-after-Free.

A simple solution is to copy the required info while inside the RCU
protected section to avoid any risk of UaF later. The address ID might
need to be modified later to handle the ID0 case later, so a copy seems
OK to deal with.

Reported-by: default avatarPaolo Abeni <pabeni@redhat.com>
Closes: https://lore.kernel.org/45cd30d3-7710-491c-ae4d-a1368c00beb1@redhat.com


Fixes: 01cacb00 ("mptcp: add netlink-based PM")
Cc: stable@vger.kernel.org
Reviewed-by: default avatarMat Martineau <martineau@kernel.org>
Signed-off-by: default avatarMatthieu Baerts (NGI0) <matttbe@kernel.org>
Link: https://patch.msgid.link/20240819-net-mptcp-pm-reusing-id-v1-14-38035d40de5b@kernel.org


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>

Conflicts:
	net/mptcp/pm_netlink.c
[Did not backport  90d93088 ("mptcp: constify a bunch of of helpers"),
85df533a787b ("mptcp: pm: do not ignore 'subflow' if 'signal' flag is
 also set"), 86e39e04 ("mptcp: keep track of local endpoint still
 available for each msk").]
Signed-off-by: default avatarLiu Jian <liujian56@huawei.com>
parent 3e6d44ea
Loading
Loading
Loading
Loading
+25 −19
Original line number Diff line number Diff line
@@ -128,11 +128,13 @@ static bool lookup_subflow_by_saddr(const struct list_head *list,
	return false;
}

static struct mptcp_pm_addr_entry *
static bool
select_local_address(const struct pm_nl_pernet *pernet,
		     struct mptcp_sock *msk)
		     struct mptcp_sock *msk,
		     struct mptcp_pm_addr_entry *new_entry)
{
	struct mptcp_pm_addr_entry *entry, *ret = NULL;
	struct mptcp_pm_addr_entry *entry;
	bool found = false;

	rcu_read_lock();
	spin_lock_bh(&msk->join_list_lock);
@@ -146,19 +148,23 @@ select_local_address(const struct pm_nl_pernet *pernet,
		if (entry->addr.family == ((struct sock *)msk)->sk_family &&
		    !lookup_subflow_by_saddr(&msk->conn_list, &entry->addr) &&
		    !lookup_subflow_by_saddr(&msk->join_list, &entry->addr)) {
			ret = entry;
			*new_entry = *entry;
			found = true;
			break;
		}
	}
	spin_unlock_bh(&msk->join_list_lock);
	rcu_read_unlock();
	return ret;

	return found;
}

static struct mptcp_pm_addr_entry *
select_signal_address(struct pm_nl_pernet *pernet, unsigned int pos)
static bool
select_signal_address(struct pm_nl_pernet *pernet, unsigned int pos,
		      struct mptcp_pm_addr_entry *new_entry)
{
	struct mptcp_pm_addr_entry *entry, *ret = NULL;
	struct mptcp_pm_addr_entry *entry;
	bool found = false;
	int i = 0;

	rcu_read_lock();
@@ -171,12 +177,14 @@ select_signal_address(struct pm_nl_pernet *pernet, unsigned int pos)
		if (!(entry->addr.flags & MPTCP_PM_ADDR_FLAG_SIGNAL))
			continue;
		if (i++ == pos) {
			ret = entry;
			*new_entry = *entry;
			found = true;
			break;
		}
	}
	rcu_read_unlock();
	return ret;

	return found;
}

static void check_work_pending(struct mptcp_sock *msk)
@@ -306,7 +314,7 @@ static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk)
{
	struct mptcp_addr_info remote = { 0 };
	struct sock *sk = (struct sock *)msk;
	struct mptcp_pm_addr_entry *local;
	struct mptcp_pm_addr_entry local;
	struct pm_nl_pernet *pernet;

	pernet = net_generic(sock_net((struct sock *)msk), pm_nl_pernet_id);
@@ -318,13 +326,12 @@ static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk)

	/* check first for announce */
	if (msk->pm.add_addr_signaled < msk->pm.add_addr_signal_max) {
		local = select_signal_address(pernet,
					      msk->pm.add_addr_signaled);

		if (local) {
			if (mptcp_pm_alloc_anno_list(msk, local)) {
		if (select_signal_address(pernet,
				msk->pm.add_addr_signaled, &local)) {
			if (mptcp_pm_alloc_anno_list(msk, &local)) {
				msk->pm.add_addr_signaled++;
				mptcp_pm_announce_addr(msk, &local->addr, false);
				mptcp_pm_announce_addr(msk, &local.addr, false);
			}
		} else {
			/* pick failed, avoid fourther attempts later */
@@ -339,13 +346,12 @@ static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk)
	    msk->pm.subflows < msk->pm.subflows_max) {
		remote_address((struct sock_common *)sk, &remote);

		local = select_local_address(pernet, msk);
		if (local) {
		if (select_local_address(pernet, msk, &local)) {
			msk->pm.local_addr_used++;
			msk->pm.subflows++;
			check_work_pending(msk);
			spin_unlock_bh(&msk->pm.lock);
			__mptcp_subflow_connect(sk, &local->addr, &remote);
			__mptcp_subflow_connect(sk, &local.addr, &remote);
			spin_lock_bh(&msk->pm.lock);
			return;
		}