Commit 3349c272 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'tipc-fix-two-race-issues-in-tipc_conn_alloc'

Xin Long says:

====================
tipc: fix two race issues in tipc_conn_alloc

The race exists beteen tipc_topsrv_accept() and tipc_conn_close(),
one is allocating the con while the other is freeing it and there
is no proper lock protecting it. Therefore, a null-pointer-defer
and a use-after-free may be triggered, see details on each patch.
====================

Link: https://lore.kernel.org/r/cover.1668807842.git.lucien.xin@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 1f0dd412 a7b42969
Loading
Loading
Loading
Loading
+11 −9
Original line number Diff line number Diff line
@@ -176,7 +176,7 @@ static void tipc_conn_close(struct tipc_conn *con)
	conn_put(con);
}

static struct tipc_conn *tipc_conn_alloc(struct tipc_topsrv *s)
static struct tipc_conn *tipc_conn_alloc(struct tipc_topsrv *s, struct socket *sock)
{
	struct tipc_conn *con;
	int ret;
@@ -202,10 +202,12 @@ static struct tipc_conn *tipc_conn_alloc(struct tipc_topsrv *s)
	}
	con->conid = ret;
	s->idr_in_use++;
	spin_unlock_bh(&s->idr_lock);

	set_bit(CF_CONNECTED, &con->flags);
	con->server = s;
	con->sock = sock;
	conn_get(con);
	spin_unlock_bh(&s->idr_lock);

	return con;
}
@@ -467,7 +469,7 @@ static void tipc_topsrv_accept(struct work_struct *work)
		ret = kernel_accept(lsock, &newsock, O_NONBLOCK);
		if (ret < 0)
			return;
		con = tipc_conn_alloc(srv);
		con = tipc_conn_alloc(srv, newsock);
		if (IS_ERR(con)) {
			ret = PTR_ERR(con);
			sock_release(newsock);
@@ -479,11 +481,11 @@ static void tipc_topsrv_accept(struct work_struct *work)
		newsk->sk_data_ready = tipc_conn_data_ready;
		newsk->sk_write_space = tipc_conn_write_space;
		newsk->sk_user_data = con;
		con->sock = newsock;
		write_unlock_bh(&newsk->sk_callback_lock);

		/* Wake up receive process in case of 'SYN+' message */
		newsk->sk_data_ready(newsk);
		conn_put(con);
	}
}

@@ -577,17 +579,17 @@ bool tipc_topsrv_kern_subscr(struct net *net, u32 port, u32 type, u32 lower,
	sub.filter = filter;
	*(u64 *)&sub.usr_handle = (u64)port;

	con = tipc_conn_alloc(tipc_topsrv(net));
	con = tipc_conn_alloc(tipc_topsrv(net), NULL);
	if (IS_ERR(con))
		return false;

	*conid = con->conid;
	con->sock = NULL;
	rc = tipc_conn_rcv_sub(tipc_topsrv(net), con, &sub);
	if (rc >= 0)
		return true;
	if (rc)
		conn_put(con);
	return false;

	conn_put(con);
	return !rc;
}

void tipc_topsrv_kern_unsubscr(struct net *net, int conid)