neighbour: fix data-races around n->output
authorEric Dumazet <edumazet@google.com>
Thu, 21 Sep 2023 09:27:13 +0000 (09:27 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 1 Oct 2023 16:14:37 +0000 (17:14 +0100)
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>
include/net/neighbour.h
net/bridge/br_netfilter_hooks.c
net/core/neighbour.c

index 6da68886fabbcb33a63ef256242d51a618571c95..07022bb0d44d4b5eef5812cc86e042833cf3a337 100644 (file)
@@ -539,7 +539,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 15186247b59af52062907cacec994e736ddbc01b..033034d68f1f057349acdd2f127c427195be6b62 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 7212c7e521ef6388f450f3882077e26088bb3644..9c09f091cbffe59043ce4614423d7dce7d4ec42a 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();
@@ -3155,7 +3155,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) {