Commit 5baa0433 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

neighbour: fix data-races around n->output

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

Add missing annotations to prevent load-store tearing.

Fixes: 1da177e4 ("Linux-2.6.12-rc2")
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Reviewed-by: default avatarDavid Ahern <dsahern@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 25563b58
...@@ -539,7 +539,7 @@ static inline int neigh_output(struct neighbour *n, struct sk_buff *skb, ...@@ -539,7 +539,7 @@ static inline int neigh_output(struct neighbour *n, struct sk_buff *skb,
READ_ONCE(hh->hh_len)) READ_ONCE(hh->hh_len))
return neigh_hh_output(hh, skb); return neigh_hh_output(hh, skb);
return n->output(n, skb); return READ_ONCE(n->output)(n, skb);
} }
static inline struct neighbour * static inline struct neighbour *
......
...@@ -294,7 +294,7 @@ int br_nf_pre_routing_finish_bridge(struct net *net, struct sock *sk, struct sk_ ...@@ -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 */ /* tell br_dev_xmit to continue with forwarding */
nf_bridge->bridged_dnat = 1; nf_bridge->bridged_dnat = 1;
/* FIXME Need to refragment */ /* FIXME Need to refragment */
ret = neigh->output(neigh, skb); ret = READ_ONCE(neigh->output)(neigh, skb);
} }
neigh_release(neigh); neigh_release(neigh);
return ret; return ret;
......
...@@ -410,7 +410,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev, ...@@ -410,7 +410,7 @@ static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev,
*/ */
__skb_queue_purge(&n->arp_queue); __skb_queue_purge(&n->arp_queue);
n->arp_queue_len_bytes = 0; n->arp_queue_len_bytes = 0;
n->output = neigh_blackhole; WRITE_ONCE(n->output, neigh_blackhole);
if (n->nud_state & NUD_VALID) if (n->nud_state & NUD_VALID)
n->nud_state = NUD_NOARP; n->nud_state = NUD_NOARP;
else else
...@@ -920,7 +920,7 @@ static void neigh_suspect(struct neighbour *neigh) ...@@ -920,7 +920,7 @@ static void neigh_suspect(struct neighbour *neigh)
{ {
neigh_dbg(2, "neigh %p is suspected\n", 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; /* Neighbour state is OK;
...@@ -932,7 +932,7 @@ static void neigh_connect(struct neighbour *neigh) ...@@ -932,7 +932,7 @@ static void neigh_connect(struct neighbour *neigh)
{ {
neigh_dbg(2, "neigh %p is connected\n", 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) static void neigh_periodic_work(struct work_struct *work)
...@@ -1449,7 +1449,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, ...@@ -1449,7 +1449,7 @@ static int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
if (n2) if (n2)
n1 = n2; n1 = n2;
} }
n1->output(n1, skb); READ_ONCE(n1->output)(n1, skb);
if (n2) if (n2)
neigh_release(n2); neigh_release(n2);
rcu_read_unlock(); rcu_read_unlock();
...@@ -3155,7 +3155,7 @@ int neigh_xmit(int index, struct net_device *dev, ...@@ -3155,7 +3155,7 @@ int neigh_xmit(int index, struct net_device *dev,
rcu_read_unlock(); rcu_read_unlock();
goto out_kfree_skb; goto out_kfree_skb;
} }
err = neigh->output(neigh, skb); err = READ_ONCE(neigh->output)(neigh, skb);
rcu_read_unlock(); rcu_read_unlock();
} }
else if (index == NEIGH_LINK_TABLE) { else if (index == NEIGH_LINK_TABLE) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment