neighbour: fix data-races around n->output
authorEric Dumazet <edumazet@google.com>
Thu, 21 Sep 2023 09:27:13 +0000 (09:27 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 10 Oct 2023 20:00:42 +0000 (22:00 +0200)
[ Upstream commit 5baa0433a15eadd729625004c37463acb982eca7 ]

n->output field can be read locklessly, while a writer
might change the pointer concurrently.

Add missing annotations to prevent load-store tearing.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
include/net/neighbour.h
net/bridge/br_netfilter_hooks.c
net/core/neighbour.c

index f6a8ecc6b1fa7cf8cee4eac9193efeae046e75e6..ccc4a0f8b4ad8ba98e04fff7c559122b93224527 100644 (file)
@@ -541,7 +541,7 @@ static inline int neigh_output(struct neighbour *n, struct sk_buff *skb,
            READ_ONCE(hh->hh_len))
                return neigh_hh_output(hh, skb);
 
-       return n->output(n, skb);
+       return READ_ONCE(n->output)(n, skb);
 }
 
 static inline struct neighbour *
index 442198cb83909ee240a50b9b523fb8921c68457a..01d690d9fe5f8767fd2138efc6624b1e85808180 100644 (file)
@@ -294,7 +294,7 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_
                        /* tell br_dev_xmit to continue with forwarding */
                        nf_bridge->bridged_dnat = 1;
                        /* FIXME Need to refragment */
-                       ret = neigh->output(neigh, skb);
+                       ret = READ_ONCE(neigh->output)(neigh, skb);
                }
                neigh_release(neigh);
                return ret;
index bafd72e5f5886b9311fedece5efb497b455c7c67..b20c9768d9f3fab9d7328ec27cc029ec999c36b0 100644 (file)
@@ -410,7 +410,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev,
                                 */
                                __skb_queue_purge(&n->arp_queue);
                                n->arp_queue_len_bytes = 0;
-                               n->output = neigh_blackhole;
+                               WRITE_ONCE(n->output, neigh_blackhole);
                                if (n->nud_state & NUD_VALID)
                                        n->nud_state = NUD_NOARP;
                                else
@@ -920,7 +920,7 @@ static void neigh_suspect(struct neighbour *neigh)
 {
        neigh_dbg(2, "neigh %p is suspected\n", neigh);
 
-       neigh->output = neigh->ops->output;
+       WRITE_ONCE(neigh->output, neigh->ops->output);
 }
 
 /* Neighbour state is OK;
@@ -932,7 +932,7 @@ static void neigh_connect(struct neighbour *neigh)
 {
        neigh_dbg(2, "neigh %p is connected\n", neigh);
 
-       neigh->output = neigh->ops->connected_output;
+       WRITE_ONCE(neigh->output, neigh->ops->connected_output);
 }
 
 static void neigh_periodic_work(struct work_struct *work)
@@ -1449,7 +1449,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
                                if (n2)
                                        n1 = n2;
                        }
-                       n1->output(n1, skb);
+                       READ_ONCE(n1->output)(n1, skb);
                        if (n2)
                                neigh_release(n2);
                        rcu_read_unlock();
@@ -3145,7 +3145,7 @@ int neigh_xmit(int index, struct net_device *dev,
                        rcu_read_unlock();
                        goto out_kfree_skb;
                }
-               err = neigh->output(neigh, skb);
+               err = READ_ONCE(neigh->output)(neigh, skb);
                rcu_read_unlock();
        }
        else if (index == NEIGH_LINK_TABLE) {