提交 aadf31de 编写于 作者: B Ben Hutchings 提交者: David S. Miller

llc: Fix races between llc2 handler use and (un)registration

When registering the handlers, any state they rely on must be
completely initialised first.  When unregistering, we must wait until
they are definitely no longer running.  llc_rcv() must also avoid
reading the handler pointers again after checking for NULL.
Signed-off-by: NBen Hutchings <ben@decadent.org.uk>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 f4f8720f
......@@ -42,6 +42,7 @@ static void (*llc_type_handlers[2])(struct llc_sap *sap,
void llc_add_pack(int type, void (*handler)(struct llc_sap *sap,
struct sk_buff *skb))
{
smp_wmb(); /* ensure initialisation is complete before it's called */
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
llc_type_handlers[type - 1] = handler;
}
......@@ -50,11 +51,19 @@ void llc_remove_pack(int type)
{
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
llc_type_handlers[type - 1] = NULL;
synchronize_net();
}
void llc_set_station_handler(void (*handler)(struct sk_buff *skb))
{
/* Ensure initialisation is complete before it's called */
if (handler)
smp_wmb();
llc_station_handler = handler;
if (!handler)
synchronize_net();
}
/**
......@@ -150,6 +159,8 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
int dest;
int (*rcv)(struct sk_buff *, struct net_device *,
struct packet_type *, struct net_device *);
void (*sta_handler)(struct sk_buff *skb);
void (*sap_handler)(struct llc_sap *sap, struct sk_buff *skb);
if (!net_eq(dev_net(dev), &init_net))
goto drop;
......@@ -182,7 +193,8 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
*/
rcv = rcu_dereference(sap->rcv_func);
dest = llc_pdu_type(skb);
if (unlikely(!dest || !llc_type_handlers[dest - 1])) {
sap_handler = dest ? ACCESS_ONCE(llc_type_handlers[dest - 1]) : NULL;
if (unlikely(!sap_handler)) {
if (rcv)
rcv(skb, dev, pt, orig_dev);
else
......@@ -193,7 +205,7 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
if (cskb)
rcv(cskb, dev, pt, orig_dev);
}
llc_type_handlers[dest - 1](sap, skb);
sap_handler(sap, skb);
}
llc_sap_put(sap);
out:
......@@ -202,9 +214,10 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
kfree_skb(skb);
goto out;
handle_station:
if (!llc_station_handler)
sta_handler = ACCESS_ONCE(llc_station_handler);
if (!sta_handler)
goto drop;
llc_station_handler(skb);
sta_handler(skb);
goto out;
}
......
......@@ -696,9 +696,9 @@ void __init llc_station_init(void)
(unsigned long)&llc_main_station);
llc_main_station.ack_timer.expires = jiffies +
sysctl_llc_station_ack_timeout;
llc_set_station_handler(llc_station_rcv);
llc_main_station.maximum_retry = 1;
llc_main_station.state = LLC_STATION_STATE_UP;
llc_set_station_handler(llc_station_rcv);
}
void llc_station_exit(void)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册