]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
neighbour: Move neigh_find_table() to neigh_get().
authorKuniyuki Iwashima <kuniyu@google.com>
Wed, 16 Jul 2025 22:08:09 +0000 (22:08 +0000)
committerJakub Kicinski <kuba@kernel.org>
Thu, 17 Jul 2025 23:25:20 +0000 (16:25 -0700)
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: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20250716221221.442239-5-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/core/neighbour.c

index 761593ea8c919f11ccdeac7a56eff35df096bfea..ffb8d80328ed76cbc855d28ad6d18afba27b03d5 100644 (file)
@@ -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) {