Skip to content

Commit 0fc906e

Browse files
TaeheeYoogregkh
authored andcommitted
hsr: fix a race condition in node list insertion and deletion
[ Upstream commit 92a3567 ] hsr nodes are protected by RCU and there is no write side lock. But node insertions and deletions could be being operated concurrently. So write side locking is needed. Test commands: ip netns add nst ip link add veth0 type veth peer name veth1 ip link add veth2 type veth peer name veth3 ip link set veth1 netns nst ip link set veth3 netns nst ip link set veth0 up ip link set veth2 up ip link add hsr0 type hsr slave1 veth0 slave2 veth2 ip a a 192.168.100.1/24 dev hsr0 ip link set hsr0 up ip netns exec nst ip link set veth1 up ip netns exec nst ip link set veth3 up ip netns exec nst ip link add hsr1 type hsr slave1 veth1 slave2 veth3 ip netns exec nst ip a a 192.168.100.2/24 dev hsr1 ip netns exec nst ip link set hsr1 up for i in {0..9} do for j in {0..9} do for k in {0..9} do for l in {0..9} do arping 192.168.100.2 -I hsr0 -s 00:01:3$i:4$j:5$k:6$l -c1 & done done done done Splat looks like: [ 236.066091][ T3286] list_add corruption. next->prev should be prev (ffff8880a5940300), but was ffff8880a5940d0. [ 236.069617][ T3286] ------------[ cut here ]------------ [ 236.070545][ T3286] kernel BUG at lib/list_debug.c:25! [ 236.071391][ T3286] invalid opcode: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN PTI [ 236.072343][ T3286] CPU: 0 PID: 3286 Comm: arping Tainted: G W 5.5.0-rc1+ #209 [ 236.073463][ T3286] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006 [ 236.074695][ T3286] RIP: 0010:__list_add_valid+0x74/0xd0 [ 236.075499][ T3286] Code: 48 39 da 75 27 48 39 f5 74 36 48 39 dd 74 31 48 83 c4 08 b8 01 00 00 00 5b 5d c3 48 b [ 236.078277][ T3286] RSP: 0018:ffff8880aaa97648 EFLAGS: 00010286 [ 236.086991][ T3286] RAX: 0000000000000075 RBX: ffff8880d4624c20 RCX: 0000000000000000 [ 236.088000][ T3286] RDX: 0000000000000075 RSI: 0000000000000008 RDI: ffffed1015552ebf [ 236.098897][ T3286] RBP: ffff88809b53d200 R08: ffffed101b3c04f9 R09: ffffed101b3c04f9 [ 236.099960][ T3286] R10: 00000000308769a1 R11: ffffed101b3c04f8 R12: ffff8880d4624c28 [ 236.100974][ T3286] R13: ffff8880d4624c20 R14: 0000000040310100 R15: ffff8880ce17ee02 [ 236.138967][ T3286] FS: 00007f23479fa680(0000) GS:ffff8880d9c00000(0000) knlGS:0000000000000000 [ 236.144852][ T3286] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 236.145720][ T3286] CR2: 00007f4a14bab210 CR3: 00000000a61c6001 CR4: 00000000000606f0 [ 236.146776][ T3286] Call Trace: [ 236.147222][ T3286] hsr_add_node+0x314/0x490 [hsr] [ 236.153633][ T3286] hsr_forward_skb+0x2b6/0x1bc0 [hsr] [ 236.154362][ T3286] ? rcu_read_lock_sched_held+0x90/0xc0 [ 236.155091][ T3286] ? rcu_read_lock_bh_held+0xa0/0xa0 [ 236.156607][ T3286] hsr_dev_xmit+0x70/0xd0 [hsr] [ 236.157254][ T3286] dev_hard_start_xmit+0x160/0x740 [ 236.157941][ T3286] __dev_queue_xmit+0x1961/0x2e10 [ 236.158565][ T3286] ? netdev_core_pick_tx+0x2e0/0x2e0 [ ... ] Reported-by: [email protected] Fixes: f421436 ("net/hsr: Add support for the High-availability Seamless Redundancy protocol (HSRv0)") Signed-off-by: Taehee Yoo <[email protected]> Signed-off-by: David S. Miller <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent fe974fb commit 0fc906e

File tree

5 files changed

+56
-37
lines changed

5 files changed

+56
-37
lines changed

net/hsr/hsr_device.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ static void hsr_dev_destroy(struct net_device *hsr_dev)
368368
del_timer_sync(&hsr->prune_timer);
369369
del_timer_sync(&hsr->announce_timer);
370370

371-
hsr_del_self_node(&hsr->self_node_db);
371+
hsr_del_self_node(hsr);
372372
hsr_del_nodes(&hsr->node_db);
373373
}
374374

@@ -440,11 +440,12 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
440440
INIT_LIST_HEAD(&hsr->ports);
441441
INIT_LIST_HEAD(&hsr->node_db);
442442
INIT_LIST_HEAD(&hsr->self_node_db);
443+
spin_lock_init(&hsr->list_lock);
443444

444445
ether_addr_copy(hsr_dev->dev_addr, slave[0]->dev_addr);
445446

446447
/* Make sure we recognize frames from ourselves in hsr_rcv() */
447-
res = hsr_create_self_node(&hsr->self_node_db, hsr_dev->dev_addr,
448+
res = hsr_create_self_node(hsr, hsr_dev->dev_addr,
448449
slave[1]->dev_addr);
449450
if (res < 0)
450451
return res;
@@ -502,7 +503,7 @@ int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2],
502503
list_for_each_entry_safe(port, tmp, &hsr->ports, port_list)
503504
hsr_del_port(port);
504505
err_add_master:
505-
hsr_del_self_node(&hsr->self_node_db);
506+
hsr_del_self_node(hsr);
506507

507508
return res;
508509
}

net/hsr/hsr_framereg.c

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,11 @@ static struct hsr_node *find_node_by_addr_A(struct list_head *node_db,
7575
/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize
7676
* frames from self that's been looped over the HSR ring.
7777
*/
78-
int hsr_create_self_node(struct list_head *self_node_db,
78+
int hsr_create_self_node(struct hsr_priv *hsr,
7979
unsigned char addr_a[ETH_ALEN],
8080
unsigned char addr_b[ETH_ALEN])
8181
{
82+
struct list_head *self_node_db = &hsr->self_node_db;
8283
struct hsr_node *node, *oldnode;
8384

8485
node = kmalloc(sizeof(*node), GFP_KERNEL);
@@ -88,33 +89,33 @@ int hsr_create_self_node(struct list_head *self_node_db,
8889
ether_addr_copy(node->macaddress_A, addr_a);
8990
ether_addr_copy(node->macaddress_B, addr_b);
9091

91-
rcu_read_lock();
92+
spin_lock_bh(&hsr->list_lock);
9293
oldnode = list_first_or_null_rcu(self_node_db,
9394
struct hsr_node, mac_list);
9495
if (oldnode) {
9596
list_replace_rcu(&oldnode->mac_list, &node->mac_list);
96-
rcu_read_unlock();
97-
synchronize_rcu();
98-
kfree(oldnode);
97+
spin_unlock_bh(&hsr->list_lock);
98+
kfree_rcu(oldnode, rcu_head);
9999
} else {
100-
rcu_read_unlock();
101100
list_add_tail_rcu(&node->mac_list, self_node_db);
101+
spin_unlock_bh(&hsr->list_lock);
102102
}
103103

104104
return 0;
105105
}
106106

107-
void hsr_del_self_node(struct list_head *self_node_db)
107+
void hsr_del_self_node(struct hsr_priv *hsr)
108108
{
109+
struct list_head *self_node_db = &hsr->self_node_db;
109110
struct hsr_node *node;
110111

111-
rcu_read_lock();
112+
spin_lock_bh(&hsr->list_lock);
112113
node = list_first_or_null_rcu(self_node_db, struct hsr_node, mac_list);
113-
rcu_read_unlock();
114114
if (node) {
115115
list_del_rcu(&node->mac_list);
116-
kfree(node);
116+
kfree_rcu(node, rcu_head);
117117
}
118+
spin_unlock_bh(&hsr->list_lock);
118119
}
119120

120121
void hsr_del_nodes(struct list_head *node_db)
@@ -130,30 +131,43 @@ void hsr_del_nodes(struct list_head *node_db)
130131
* seq_out is used to initialize filtering of outgoing duplicate frames
131132
* originating from the newly added node.
132133
*/
133-
struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[],
134-
u16 seq_out)
134+
static struct hsr_node *hsr_add_node(struct hsr_priv *hsr,
135+
struct list_head *node_db,
136+
unsigned char addr[],
137+
u16 seq_out)
135138
{
136-
struct hsr_node *node;
139+
struct hsr_node *new_node, *node;
137140
unsigned long now;
138141
int i;
139142

140-
node = kzalloc(sizeof(*node), GFP_ATOMIC);
141-
if (!node)
143+
new_node = kzalloc(sizeof(*new_node), GFP_ATOMIC);
144+
if (!new_node)
142145
return NULL;
143146

144-
ether_addr_copy(node->macaddress_A, addr);
147+
ether_addr_copy(new_node->macaddress_A, addr);
145148

146149
/* We are only interested in time diffs here, so use current jiffies
147150
* as initialization. (0 could trigger an spurious ring error warning).
148151
*/
149152
now = jiffies;
150153
for (i = 0; i < HSR_PT_PORTS; i++)
151-
node->time_in[i] = now;
154+
new_node->time_in[i] = now;
152155
for (i = 0; i < HSR_PT_PORTS; i++)
153-
node->seq_out[i] = seq_out;
154-
155-
list_add_tail_rcu(&node->mac_list, node_db);
156+
new_node->seq_out[i] = seq_out;
156157

158+
spin_lock_bh(&hsr->list_lock);
159+
list_for_each_entry_rcu(node, node_db, mac_list) {
160+
if (ether_addr_equal(node->macaddress_A, addr))
161+
goto out;
162+
if (ether_addr_equal(node->macaddress_B, addr))
163+
goto out;
164+
}
165+
list_add_tail_rcu(&new_node->mac_list, node_db);
166+
spin_unlock_bh(&hsr->list_lock);
167+
return new_node;
168+
out:
169+
spin_unlock_bh(&hsr->list_lock);
170+
kfree(new_node);
157171
return node;
158172
}
159173

@@ -163,6 +177,7 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb,
163177
bool is_sup)
164178
{
165179
struct list_head *node_db = &port->hsr->node_db;
180+
struct hsr_priv *hsr = port->hsr;
166181
struct hsr_node *node;
167182
struct ethhdr *ethhdr;
168183
u16 seq_out;
@@ -196,7 +211,7 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb,
196211
seq_out = HSR_SEQNR_START;
197212
}
198213

199-
return hsr_add_node(node_db, ethhdr->h_source, seq_out);
214+
return hsr_add_node(hsr, node_db, ethhdr->h_source, seq_out);
200215
}
201216

202217
/* Use the Supervision frame's info about an eventual macaddress_B for merging
@@ -206,10 +221,11 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb,
206221
void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
207222
struct hsr_port *port_rcv)
208223
{
209-
struct ethhdr *ethhdr;
210-
struct hsr_node *node_real;
224+
struct hsr_priv *hsr = port_rcv->hsr;
211225
struct hsr_sup_payload *hsr_sp;
226+
struct hsr_node *node_real;
212227
struct list_head *node_db;
228+
struct ethhdr *ethhdr;
213229
int i;
214230

215231
ethhdr = (struct ethhdr *)skb_mac_header(skb);
@@ -231,7 +247,7 @@ void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
231247
node_real = find_node_by_addr_A(node_db, hsr_sp->macaddress_A);
232248
if (!node_real)
233249
/* No frame received from AddrA of this node yet */
234-
node_real = hsr_add_node(node_db, hsr_sp->macaddress_A,
250+
node_real = hsr_add_node(hsr, node_db, hsr_sp->macaddress_A,
235251
HSR_SEQNR_START - 1);
236252
if (!node_real)
237253
goto done; /* No mem */
@@ -252,7 +268,9 @@ void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
252268
}
253269
node_real->addr_B_port = port_rcv->type;
254270

271+
spin_lock_bh(&hsr->list_lock);
255272
list_del_rcu(&node_curr->mac_list);
273+
spin_unlock_bh(&hsr->list_lock);
256274
kfree_rcu(node_curr, rcu_head);
257275

258276
done:
@@ -368,12 +386,13 @@ void hsr_prune_nodes(struct timer_list *t)
368386
{
369387
struct hsr_priv *hsr = from_timer(hsr, t, prune_timer);
370388
struct hsr_node *node;
389+
struct hsr_node *tmp;
371390
struct hsr_port *port;
372391
unsigned long timestamp;
373392
unsigned long time_a, time_b;
374393

375-
rcu_read_lock();
376-
list_for_each_entry_rcu(node, &hsr->node_db, mac_list) {
394+
spin_lock_bh(&hsr->list_lock);
395+
list_for_each_entry_safe(node, tmp, &hsr->node_db, mac_list) {
377396
/* Don't prune own node. Neither time_in[HSR_PT_SLAVE_A]
378397
* nor time_in[HSR_PT_SLAVE_B], will ever be updated for
379398
* the master port. Thus the master node will be repeatedly
@@ -421,7 +440,7 @@ void hsr_prune_nodes(struct timer_list *t)
421440
kfree_rcu(node, rcu_head);
422441
}
423442
}
424-
rcu_read_unlock();
443+
spin_unlock_bh(&hsr->list_lock);
425444

426445
/* Restart timer */
427446
mod_timer(&hsr->prune_timer,

net/hsr/hsr_framereg.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@
1212

1313
struct hsr_node;
1414

15-
void hsr_del_self_node(struct list_head *self_node_db);
15+
void hsr_del_self_node(struct hsr_priv *hsr);
1616
void hsr_del_nodes(struct list_head *node_db);
17-
struct hsr_node *hsr_add_node(struct list_head *node_db, unsigned char addr[],
18-
u16 seq_out);
1917
struct hsr_node *hsr_get_node(struct hsr_port *port, struct sk_buff *skb,
2018
bool is_sup);
2119
void hsr_handle_sup_frame(struct sk_buff *skb, struct hsr_node *node_curr,
@@ -33,7 +31,7 @@ int hsr_register_frame_out(struct hsr_port *port, struct hsr_node *node,
3331

3432
void hsr_prune_nodes(struct timer_list *t);
3533

36-
int hsr_create_self_node(struct list_head *self_node_db,
34+
int hsr_create_self_node(struct hsr_priv *hsr,
3735
unsigned char addr_a[ETH_ALEN],
3836
unsigned char addr_b[ETH_ALEN]);
3937

net/hsr/hsr_main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event,
6464

6565
/* Make sure we recognize frames from ourselves in hsr_rcv() */
6666
port = hsr_port_get_hsr(hsr, HSR_PT_SLAVE_B);
67-
res = hsr_create_self_node(&hsr->self_node_db,
67+
res = hsr_create_self_node(hsr,
6868
master->dev->dev_addr,
6969
port ?
7070
port->dev->dev_addr :

net/hsr/hsr_main.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,9 @@ struct hsr_priv {
160160
int announce_count;
161161
u16 sequence_nr;
162162
u16 sup_sequence_nr; /* For HSRv1 separate seq_nr for supervision */
163-
u8 prot_version; /* Indicate if HSRv0 or HSRv1. */
164-
spinlock_t seqnr_lock; /* locking for sequence_nr */
163+
u8 prot_version; /* Indicate if HSRv0 or HSRv1. */
164+
spinlock_t seqnr_lock; /* locking for sequence_nr */
165+
spinlock_t list_lock; /* locking for node list */
165166
unsigned char sup_multicast_addr[ETH_ALEN];
166167
#ifdef CONFIG_DEBUG_FS
167168
struct dentry *node_tbl_root;

0 commit comments

Comments
 (0)