Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Ben Hutchings and committed by
David S. Miller
aadf31de f4f8720f

+18 -5
+17 -4
net/llc/llc_input.c
··· 42 42 void llc_add_pack(int type, void (*handler)(struct llc_sap *sap, 43 43 struct sk_buff *skb)) 44 44 { 45 + smp_wmb(); /* ensure initialisation is complete before it's called */ 45 46 if (type == LLC_DEST_SAP || type == LLC_DEST_CONN) 46 47 llc_type_handlers[type - 1] = handler; 47 48 } ··· 51 50 { 52 51 if (type == LLC_DEST_SAP || type == LLC_DEST_CONN) 53 52 llc_type_handlers[type - 1] = NULL; 53 + synchronize_net(); 54 54 } 55 55 56 56 void llc_set_station_handler(void (*handler)(struct sk_buff *skb)) 57 57 { 58 + /* Ensure initialisation is complete before it's called */ 59 + if (handler) 60 + smp_wmb(); 61 + 58 62 llc_station_handler = handler; 63 + 64 + if (!handler) 65 + synchronize_net(); 59 66 } 60 67 61 68 /** ··· 159 150 int dest; 160 151 int (*rcv)(struct sk_buff *, struct net_device *, 161 152 struct packet_type *, struct net_device *); 153 + void (*sta_handler)(struct sk_buff *skb); 154 + void (*sap_handler)(struct llc_sap *sap, struct sk_buff *skb); 162 155 163 156 if (!net_eq(dev_net(dev), &init_net)) 164 157 goto drop; ··· 193 182 */ 194 183 rcv = rcu_dereference(sap->rcv_func); 195 184 dest = llc_pdu_type(skb); 196 - if (unlikely(!dest || !llc_type_handlers[dest - 1])) { 185 + sap_handler = dest ? ACCESS_ONCE(llc_type_handlers[dest - 1]) : NULL; 186 + if (unlikely(!sap_handler)) { 197 187 if (rcv) 198 188 rcv(skb, dev, pt, orig_dev); 199 189 else ··· 205 193 if (cskb) 206 194 rcv(cskb, dev, pt, orig_dev); 207 195 } 208 - llc_type_handlers[dest - 1](sap, skb); 196 + sap_handler(sap, skb); 209 197 } 210 198 llc_sap_put(sap); 211 199 out: ··· 214 202 kfree_skb(skb); 215 203 goto out; 216 204 handle_station: 217 - if (!llc_station_handler) 205 + sta_handler = ACCESS_ONCE(llc_station_handler); 206 + if (!sta_handler) 218 207 goto drop; 219 - llc_station_handler(skb); 208 + sta_handler(skb); 220 209 goto out; 221 210 } 222 211
+1 -1
net/llc/llc_station.c
··· 696 696 (unsigned long)&llc_main_station); 697 697 llc_main_station.ack_timer.expires = jiffies + 698 698 sysctl_llc_station_ack_timeout; 699 - llc_set_station_handler(llc_station_rcv); 700 699 llc_main_station.maximum_retry = 1; 701 700 llc_main_station.state = LLC_STATION_STATE_UP; 701 + llc_set_station_handler(llc_station_rcv); 702 702 } 703 703 704 704 void llc_station_exit(void)