Commit 0e5ac19c authored by Kuniyuki Iwashima's avatar Kuniyuki Iwashima Committed by Jakub Kicinski
Browse files

neighbour: Move neigh_find_table() to neigh_get().



neigh_valid_get_req() calls neigh_find_table() to fetch neigh_tables[].

neigh_find_table() uses rcu_dereference_rtnl(), but RTNL actually does
not protect it at all; neigh_table_clear() can be called without RTNL
and only waits for RCU readers by synchronize_rcu().

Fortunately, there is no bug because IPv4 is built-in, IPv6 cannot be
unloaded, and DECNET was removed.

To fetch neigh_tables[] by rcu_dereference() later, let's move
neigh_find_table() from neigh_valid_get_req() to neigh_get().

Signed-off-by: default avatarKuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20250716221221.442239-5-kuniyu@google.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 3dfe0b57
Loading
Loading
Loading
Loading
+20 −17
Original line number Diff line number Diff line
@@ -2911,10 +2911,9 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
}

static struct ndmsg *neigh_valid_get_req(const struct nlmsghdr *nlh,
					 struct neigh_table **tbl, void **dst,
					 struct nlattr **tb,
					 struct netlink_ext_ack *extack)
{
	struct nlattr *tb[NDA_MAX + 1];
	struct ndmsg *ndm;
	int err, i;

@@ -2945,12 +2944,6 @@ static struct ndmsg *neigh_valid_get_req(const struct nlmsghdr *nlh,
	if (err < 0)
		return ERR_PTR(err);

	*tbl = neigh_find_table(ndm->ndm_family);
	if (!*tbl) {
		NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request");
		return ERR_PTR(-EAFNOSUPPORT);
	}

	for (i = 0; i <= NDA_MAX; ++i) {
		switch (i) {
		case NDA_DST:
@@ -2958,12 +2951,6 @@ static struct ndmsg *neigh_valid_get_req(const struct nlmsghdr *nlh,
				NL_SET_ERR_ATTR_MISS(extack, NULL, NDA_DST);
				return ERR_PTR(-EINVAL);
			}

			if (nla_len(tb[i]) != (int)(*tbl)->key_len) {
				NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request");
				return ERR_PTR(-EINVAL);
			}
			*dst = nla_data(tb[i]);
			break;
		default:
			if (!tb[i])
@@ -3001,16 +2988,17 @@ static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
{
	struct net *net = sock_net(in_skb->sk);
	u32 pid = NETLINK_CB(in_skb).portid;
	struct nlattr *tb[NDA_MAX + 1];
	struct net_device *dev = NULL;
	struct neigh_table *tbl = NULL;
	u32 seq = nlh->nlmsg_seq;
	struct neigh_table *tbl;
	struct neighbour *neigh;
	struct sk_buff *skb;
	struct ndmsg *ndm;
	void *dst = NULL;
	void *dst;
	int err;

	ndm = neigh_valid_get_req(nlh, &tbl, &dst, extack);
	ndm = neigh_valid_get_req(nlh, tb, extack);
	if (IS_ERR(ndm))
		return PTR_ERR(ndm);

@@ -3021,6 +3009,21 @@ static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
	if (!skb)
		return -ENOBUFS;

	tbl = neigh_find_table(ndm->ndm_family);
	if (!tbl) {
		NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request");
		err = -EAFNOSUPPORT;
		goto err_free_skb;
	}

	if (nla_len(tb[NDA_DST]) != (int)tbl->key_len) {
		NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request");
		err = -EINVAL;
		goto err_free_skb;
	}

	dst = nla_data(tb[NDA_DST]);

	if (ndm->ndm_ifindex) {
		dev = __dev_get_by_index(net, ndm->ndm_ifindex);
		if (!dev) {